Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>; } private boolean isCallToScopeMethod(Node n) { return n.isCall() && SCOPING_METHOD_NAME.equals(n.getFirstChild().getQualifiedName()); } @Override public void enterScope(NodeTraversal t) { Node n = t.getCurrentNode().getParent(); if (n != null && isCallToScopeMethod(n)) { transformation = transformationHandler.logAliasTransformation( n.getSourceFileName(), getSourceRegion(n)); findAliases(t); } } @Override public void exitScope(NodeTraversal t) { if (t.getScopeDepth() == 2) { aliases.clear(); transformation = null; } } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction() && t.inGlobalScope()) { // Do not traverse in to functions except for goog.scope functions. if (parent == null || !isCallToScopeMethod(parent)) { return false; } } return true; } private SourcePosition<AliasTransformation> getSourceRegion(Node n) { Node testNode = n; Node next = null; for (; next != null || testNode.isScript();) { next = testNode.getNext(); testNode = testNode.getParent(); } int endLine = next == null ? Integer.MAX_VALUE : next.getLineno(); // child is the "goog.scope" and the second should be the parameter. report(t, n, GOOG_SCOPE_HAS_BAD_PARAMETERS); } else { Node anonymousFnNode = n.getChildAtIndex(1); if (!anonymousFnNode.isFunction() || NodeUtil.getFunctionName(anonymousFnNode) != null || NodeUtil.getFunctionParameters(anonymousFnNode).hasChildren()) { report(t, anonymousFnNode, GOOG_SCOPE_HAS_BAD_PARAMETERS); } else { scopeCalls.add(n); } } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (isCallToScopeMethod(n)) { validateScopeCall(t, n, n.getParent()); } if (t.getScopeDepth() < 2) { return; } int type = n.getType(); Var aliasVar = null; if (type == Token.NAME) { String name = n.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>getString(); Var lexicalVar = t.getScope().getVar(n.getString()); if (lexicalVar != null && lexicalVar == aliases.get(name)) { aliasVar = lexicalVar; } } // Validate the top-level of the goog.scope block. if (t.getScopeDepth() == 2) { if (aliasVar != null && NodeUtil.isLValue(n)) { if (aliasVar.getNode() == n) { aliasDefinitionsInOrder.add(n); // Return early, to ensure that we don't record a definition // twice. return; } else { report(t, n, GOOG_SCOPE_ALIAS_REDEFINED, n.getString()); } } if (type == Token.RETURN) { report(t, n, GOOG_SCOPE_USES_RETURN); } else if (type == Token.THIS) { report(t, n, GOOG_SCOPE_REFERENCES_THIS); } else if (type == Token.THROW) { report(t, n, GOOG_SCOPE_USES_THROW); } } // Validate all descendent scopes of the goog.scope block. if (t.getScopeDepth() >= 2) { // Check if this name points to an alias. if (aliasVar != null) { // Note, to support the transitive case, it's important we don't // clone aliasedNode here. For example, // var g = goog; var d = g.dom; d.createElement('DIV'); // The node in aliasedNode (which is "g") will be replaced in the // changes pass above with "goog". If we cloned here, we'd end up // with <code>g.dom.createElement('DIV')</code>. Node aliasedNode = aliasVar.getInitialValue(); aliasUsages.add(new AliasedNode(n, aliasedNode)); } JSDocInfo info = n.getJSDocInfo(); if (info != null) { for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set<Scope.Var> initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet<Scope.Var>(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else {

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> initializedConstants.add(var); } } } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; } case Token.INC: case Token.DEC: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { reportError(t, n, name); } } break; } } } /** * Gets whether a variable is a constant initialized to a literal value at * the point where it is declared. */ private boolean isConstant(Scope.Var var) { return var != null && var.isConst(); } /** * Reports a reassigned constant error. */ void reportError(NodeTraversal t, Node n, String name) { compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name)); } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>, this); // Code with hidden side-effect code is common, for example // accessing "el.offsetWidth" forces a reflow in browsers, to allow this // will still allowing local dead code removal in general, // protect the "side-effect free" code in the source. // if (protectSideEffectFreeCode) { protectSideEffects(); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // VOID nodes appear when there are extra semicolons at the BLOCK level. // I've been unable to think of any cases where this indicates a bug, // and apparently some people like keeping these semicolons around, // so we'll allow it. if (n.isEmpty() || n.isComma()) { return; } if (parent == null) { return; } int pt = parent.getType(); if (pt == Token.COMMA) { Node gramps = parent.getParent(); if (gramps.isCall() && parent == gramps.getFirstChild()) { // Semantically, a direct call to eval is different from an indirect // call to an eval. See ECMA-262 S15.1.2.1. So it's OK for the first // expression to a comma to be a no-op if it's used to indirect // an eval. if (n == parent.getFirstChild() && parent.getChildCount() == 2 && n.getNext().isName() && "eval".equals(n.getNext().getString())) { return; } } if (n == parent.getLastChild()) { for (Node an : parent.getAncestors()) { int ancestorType = an.getType(); if (ancestorType == Token.COMMA) continue; if (ancestorType != Token.EXPR_RESULT && ancestorType != Token.BLOCK) return; else break; } } } else if (pt != Token.EXPR_RESULT && pt != Token.BLOCK) { if (pt == Token.FOR && parent.getChildCount() == 4 && (n == parent.getFirstChild() || n == parent.getFirstChild().getNext().getNext())) { // Fall through and look for warnings for the 1st and 3rd child

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> // of a for. } else { return; // it might be OK to not have a side-effect } } boolean isSimpleOp = NodeUtil.isSimpleOperatorType(n.getType()); if (isSimpleOp || !NodeUtil.mayHaveSideEffects(n, t.getCompiler())) { if (n.isQualifiedName() && n.getJSDocInfo() != null) { // This no-op statement was there so that JSDoc information could // be attached to the name. This check should not complain about it. return; } else if (n.isExprResult()) { // we already reported the problem when we visited the child. return; } String msg = "This code lacks side-effects. Is there a bug?"; if (n.isString()) { msg = "Is there a missing '+' on the previous line?"; } else if (isSimpleOp) { msg = "The result of the '" + Token.name(n.getType()).toLowerCase() + "' operator is not being used."; } t.getCompiler().report( t.makeError(n, level, USELESS_CODE_ERROR, msg)); // TODO(johnlenz): determine if it is necessary to // try to protect side-effect free statements as well. if (!NodeUtil.isStatement(n)) { problemNodes.add(n); } } } /** * Protect side-effect free nodes by making them parameters * to a extern function call. This call will be removed * after all the optimizations passes have run. */ private void protectSideEffects() { if (!problemNodes.isEmpty()) { addExtern(); for (Node n : problemNodes) { Node name = IR.name(PROTECTOR_FN).srcref(n); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node replacement = IR.call(name).srcref(n); replacement.putBooleanProp(Node.FREE_CALL, true); n.getParent().replaceChild(n, replacement); replacement.addChildToBack(n); } compiler.reportCodeChange(); } } private void addExtern() { Node name = IR.name(PROTECTOR_FN); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node var = IR.var(name); // Add "@noalias" so we

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations(compiler)); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateFunctions(n, parent); annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>ging function for non-equality between types. */ private static final Function<TypePair, TypePair> NE = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderInequality(p.typeB); } }; /** * Merging function for strict equality between types. */ private static final Function<TypePair, TypePair> SHEQ = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowEquality(p.typeB); } }; /** * Merging function for strict non-equality between types. */ private static final Function<TypePair, TypePair> SHNE = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowInequality(p.typeB); } }; /** * Merging function for inequality comparisons between types. */ private final Function<TypePair, TypePair> INEQ = new Function<TypePair, TypePair>() { @Override public TypePair apply(TypePair p) { return new TypePair( getRestrictedWithoutUndefined(p.typeA), getRestrictedWithoutUndefined(p.typeB)); } }; /** * Creates a semantic reverse abstract interpreter. */ public SemanticReverseAbstractInterpreter(CodingConvention convention, JSTypeRegistry typeRegistry) { super(convention, typeRegistry); } @Override public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { // Check for the typeof operator. int operatorToken = condition.getType(); switch (operatorToken) { case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.isTypeOf() && right.isString()) { typeOfNode = left; stringNode = right; } else if (right.isTypeOf() && left.isString()) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE); } case Token.NE: if (outcome) { return caseEquality(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, S

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>HEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().isString()) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality(Node left, Node right, FlowScope blindScope, Function<TypePair, TypePair> merging) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.get

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler) { this.compiler = compiler; } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING_KEY)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent, gramps)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype")) { return false; } } } if (parent != null && parent.isAssign()) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>assignLhsChild == null) { assignLhsChild = lhs; } } else { // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (NodeUtil.isGet(lhs)) { if (lhs.isGetProp() && lhs.getLastChild().getString().equals("prototype")) { return false; } Node llhs = lhs.getFirstChild(); if (llhs.isGetProp() && llhs.getLastChild().getString().equals("prototype")) { return false; } } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isThis() && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. return parent != null && NodeUtil.isGet(parent); } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} * ... x = function() {}; * var ... x = function() {}; * ... var x = function() {}; * </pre> */ private JSDocInfo getFunctionJsDocInfo(Node n) { JSDocInfo jsDoc = n.getJSDocInfo(); Node parent = n.getParent(); if (jsDoc == null) { int parentType = parent.getType(); if (parentType == Token.NAME || parentType == Token.ASSIGN) { jsDoc = parent.getJSDocInfo(); if (jsDoc == null && parentType == Token.NAME) { Node gramps = parent.getParent(); if (gramps.isVar()) { jsDoc = gramps.getJSDocInfo(); } } } } return jsDoc; } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.isName()); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.isCall()) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, especially because // the intended use of defines is with config_files, where // all the defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current input module. */ public JSModule getModule() { CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List<Node> roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } public static void traverseRoots( AbstractCompiler compiler, Callback cb, Node ... roots) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } /** * Traverses a branch. */ @SuppressWarnings("fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); source

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Name = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.isBlock(), body); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.common.base.Preconditions; import java.util.List; /** * An AST construction helper class * @author johnlenz@google.com (John Lenz) */ public class IR { private IR() {} public static Node empty() { return new Node(Token.EMPTY); } public static Node function(Node name, Node params, Node body) { Preconditions.checkState(name.isName()); Preconditions.checkState(params.isParamList()); Preconditions.checkState(body.isBlock()); return new Node(Token.FUNCTION, name, params, body); }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> public static Node paramList() { return new Node(Token.PARAM_LIST); } public static Node paramList(Node param) { Preconditions.checkState(param.isName()); return new Node(Token.PARAM_LIST, param); } public static Node paramList(Node ... params) { Node paramList = paramList(); for (Node param : params) { Preconditions.checkState(param.isName()); paramList.addChildToBack(param); } return paramList; } public static Node paramList(List<Node> params) { Node paramList = paramList(); for (Node param : params) { Preconditions.checkState(param.isName()); paramList.addChildToBack(param); } return paramList; } public static Node block() { Node block = new Node(Token.BLOCK); return block; } public static Node block(Node stmt) { Preconditions.checkState(mayBeStatement(stmt)); Node block = new Node(Token.BLOCK, stmt); return block; } public static Node block(Node ... stmts) { Node block = new Node(Token.BLOCK); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatement(stmt)); block.addChildToBack(stmt); } return block; } private static Node blockUnchecked(Node stmt) { return new Node(Token.BLOCK, stmt); } public static Node script(Node ... stmts) { // TODO(johnlenz): finish setting up the SCRIPT node Node block = new Node(Token.SCRIPT); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatement(stmt)); block.addChildToBack(stmt); } return block; } public static Node var(Node name, Node value) { Preconditions.checkState(name.isName() && !name.hasChildren()); Preconditions.checkState(mayBeExpression(value)); name.addChildToFront(value); return var(name); } public static Node var(Node name) { Preconditions.checkState(name.isName()); return new Node(Token.VAR, name); } public static Node returnNode() { return new Node(Token.RETURN); } public static Node returnNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.RETURN, expr);

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> } public static Node throwNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.THROW, expr); } public static Node exprResult(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.EXPR_RESULT, expr); } public static Node ifNode(Node cond, Node then) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); return new Node(Token.IF, cond, then); } public static Node ifNode(Node cond, Node then, Node elseNode) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); Preconditions.checkState(elseNode.isBlock()); return new Node(Token.IF, cond, then, elseNode); } public static Node doNode(Node body, Node cond) { Preconditions.checkState(body.isBlock()); Preconditions.checkState(mayBeExpression(cond)); return new Node(Token.DO, body, cond); } public static Node forIn(Node target, Node cond, Node body) { Preconditions.checkState(target.isVar() || mayBeExpression(target)); Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(body.isBlock()); return new Node(Token.FOR, target, cond, body); } public static Node forNode(Node init, Node cond, Node incr, Node body) { Preconditions.checkState(init.isVar() || mayBeExpressionOrEmpty(init)); Preconditions.checkState(mayBeExpressionOrEmpty(cond)); Preconditions.checkState(mayBeExpressionOrEmpty(incr)); Preconditions.checkState(body.isBlock()); return new Node(Token.FOR, init, cond, incr, body); } public static Node switchNode(Node cond, Node ... cases) { Preconditions.checkState(mayBeExpression(cond)); Node switchNode = new Node(Token.SWITCH, cond); for (Node caseNode : cases) { Preconditions.checkState(caseNode.isCase() || caseNode.isDefaultCase()); switchNode.addChildToBack(caseNode); } return switchNode; } public static Node caseNode(Node expr, Node body) { Preconditions.checkState(mayBeExpression(expr)); Preconditions.checkState(body.isBlock()); body.putBoolean

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Prop(Node.SYNTHETIC_BLOCK_PROP, true); return new Node(Token.CASE, expr, body); } public static Node defaultCase(Node body) { Preconditions.checkState(body.isBlock()); body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); return new Node(Token.DEFAULT_CASE, body); } public static Node label(Node name, Node stmt) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); Preconditions.checkState(mayBeStatement(stmt)); Node block = new Node(Token.LABEL, name, stmt); return block; } public static Node labelName(String name) { Preconditions.checkState(!name.isEmpty()); return Node.newString(Token.LABEL_NAME, name); } public static Node tryFinally(Node tryBody, Node finallyBody) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(finallyBody.isBlock()); Node catchBody = block().copyInformationFrom(tryBody); return new Node(Token.TRY, tryBody, catchBody, finallyBody); } public static Node tryCatch(Node tryBody, Node catchNode) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(catchNode.isCatch()); Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode); return new Node(Token.TRY, tryBody, catchBody); } public static Node tryCatchFinally( Node tryBody, Node catchNode, Node finallyBody) { Preconditions.checkState(finallyBody.isBlock()); Node tryNode = tryCatch(tryBody, catchNode); tryNode.addChildToBack(finallyBody); return tryNode; } public static Node catchNode(Node expr, Node body) { Preconditions.checkState(expr.isName()); Preconditions.checkState(body.isBlock()); return new Node(Token.CATCH, expr, body); } public static Node breakNode() { return new Node(Token.BREAK); } public static Node breakNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.BREAK, name); } public static Node continueNode() { return new Node(Token.CONTINUE); } public static Node continueNode

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.CONTINUE, name); } // public static Node call(Node target, Node ... args) { Node call = new Node(Token.CALL, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); call.addChildToBack(arg); } return call; } public static Node newNode(Node target, Node ... args) { Node newcall = new Node(Token.NEW, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); newcall.addChildToBack(arg); } return newcall; } public static Node name(String name) { return Node.newString(Token.NAME, name); } public static Node getprop(Node target, Node prop) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(prop.isString()); return new Node(Token.GETPROP, target, prop); } public static Node getelem(Node target, Node elem) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(mayBeExpression(elem)); return new Node(Token.GETELEM, target, elem); } public static Node assign(Node target, Node expr) { Preconditions.checkState(isAssignmentTarget(target)); Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.ASSIGN, target, expr); } public static Node hook(Node cond, Node trueval, Node falseval) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(mayBeExpression(trueval)); Preconditions.checkState(mayBeExpression(falseval)); return new Node(Token.HOOK, cond, trueval, falseval); } public static Node comma(Node expr1, Node expr2) { return binaryOp(Token.COMMA, expr1, expr2); } public static Node and(Node expr1, Node expr2) { return binaryOp(Token.AND, expr1, expr2); } public static Node or(Node expr1, Node expr2) { return binaryOp(Token.OR, expr1, expr2); } public static Node not(Node expr1) { return unaryOp(Token.NOT, expr1

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>); } /** * "==" */ public static Node eq(Node expr1, Node expr2) { return binaryOp(Token.EQ, expr1, expr2); } /** * "===" */ public static Node sheq(Node expr1, Node expr2) { return binaryOp(Token.SHEQ, expr1, expr2); } public static Node voidNode(Node expr1) { return unaryOp(Token.VOID, expr1); } public static Node neg(Node expr1) { return unaryOp(Token.NEG, expr1); } public static Node pos(Node expr1) { return unaryOp(Token.POS, expr1); } public static Node add(Node expr1, Node expr2) { return binaryOp(Token.ADD, expr1, expr2); } public static Node sub(Node expr1, Node expr2) { return binaryOp(Token.SUB, expr1, expr2); } // TODO(johnlenz): the rest of the ops // literals public static Node objectlit(Node ... propdefs) { Node objectlit = new Node(Token.OBJECTLIT); for (Node propdef : propdefs) { Preconditions.checkState( propdef.isStringKey() || propdef.isGetterDef() || propdef.isSetterDef()); Preconditions.checkState(propdef.hasOneChild()); objectlit.addChildToBack(propdef); } return objectlit; } // TODO(johnlenz): quoted props public static Node propdef(Node string, Node value) { Preconditions.checkState(string.isStringKey()); Preconditions.checkState(!string.hasChildren()); Preconditions.checkState(mayBeExpression(value)); string.addChildToFront(value); return string; } public static Node arraylit(Node ... exprs) { Node arraylit = new Node(Token.ARRAYLIT); for (Node expr : exprs) { Preconditions.checkState(mayBeExpressionOrEmpty(expr)); arraylit.addChildToBack(expr); } return arraylit; } public static Node regexp(Node expr) { Preconditions.checkState(expr.isString()); return new Node(Token.REGEXP, expr); } public static Node regexp(Node expr, Node flags) { Preconditions.checkState(expr.isString()); Preconditions.checkState(flags.isString()); return new Node

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>(Token.REGEXP, expr, flags); } public static Node string(String s) { return Node.newString(s); } public static Node stringKey(String s) { return Node.newString(Token.STRING_KEY, s); } public static Node number(double d) { return Node.newNumber(d); } public static Node thisNode() { return new Node(Token.THIS); } public static Node trueNode() { return new Node(Token.TRUE); } public static Node falseNode() { return new Node(Token.FALSE); } public static Node nullNode() { return new Node(Token.NULL); } // helper methods private static Node binaryOp(int token, Node expr1, Node expr2) { Preconditions.checkState(mayBeExpression(expr1)); Preconditions.checkState(mayBeExpression(expr2)); return new Node(token, expr1, expr2); } private static Node unaryOp(int token, Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(token, expr); } private static boolean mayBeExpressionOrEmpty(Node n) { return n.isEmpty() || mayBeExpression(n); } private static boolean isAssignmentTarget(Node n) { return n.isName() || n.isGetProp() || n.isGetElem(); } // NOTE: some nodes are neither statements nor expression nodes: // SCRIPT, LABEL_NAME, PARAM_LIST, CASE, DEFAULT_CASE, CATCH // GETTER_DEF, SETTER_DEF /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeStatement(Node n) { switch (n.getType()) { case Token.EMPTY: case Token.FUNCTION: // EMPTY and FUNCTION are used both in expression and statement // contexts return true; case Token.BLOCK: case Token.BREAK: case Token.CONST: case Token.CONTINUE: case Token.DEBUGGER: case Token.DO: case Token.EXPR_RESULT: case Token.FOR: case Token.IF: case Token.LABEL: case Token.RETURN: case Token.SWITCH: case Token.THROW: case Token.TRY: case Token.VAR: case Token.WHILE: case Token.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>WITH: return true; default: return false; } } /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeExpression(Node n) { switch (n.getType()) { case Token.FUNCTION: // FUNCTION is used both in expression and statement // contexts. return true; case Token.ADD: case Token.AND: case Token.ARRAYLIT: case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.BITAND: case Token.BITOR: case Token.BITNOT: case Token.BITXOR: case Token.CALL: case Token.COMMA: case Token.DEC: case Token.DELPROP: case Token.DIV: case Token.EQ: case Token.FALSE: case Token.GE: case Token.GETPROP: case Token.GETELEM: case Token.GT: case Token.HOOK: case Token.IN: case Token.INC: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NAME: case Token.NE: case Token.NEG: case Token.NEW: case Token.NOT: case Token.NUMBER: case Token.NULL: case Token.OBJECTLIT: case Token.OR: case Token.POS: case Token.REGEXP: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.STRING: case Token.SUB: case Token.THIS: case Token.TYPEOF: case Token.TRUE: case Token.URSH: case Token.VOID: return true; default: return false; } } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * Create a new variable in a synthetic script. This will prevent * subsequent compiler passes from crashing. */ private void createSynthesizedExternVar(String varName) { Node nameNode = IR.name(varName); // Mark the variable as constant if it matches the coding convention // for constant vars. // NOTE(nicksantos): honestly, I'm not sure how much this matters. // AFAIK, all people who use the CONST coding convention also // compile with undeclaredVars as errors. We have some test // cases for this configuration though, and it makes them happier. if (compiler.getCodingConvention().isConstant(varName)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } getSynthesizedExternsRoot().addChildToBack( IR.var(nameNode)); varsToDeclareInExterns.remove(varName); compiler.reportCodeChange(); } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.PARAM_LIST: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); varsToDeclareInEx

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(t, v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block stack and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explicit CATCH token but no explicit // FINALLY token. For simplicity,

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(NodeTraversal t, Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference, t, v); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map<Var, ReferenceCollection> referenceMap; public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); } } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); } static Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} }; /** * A collection of references. Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection implements Iterable<Reference> { List<Reference> references = Lists.newArrayList(); @Override public Iterator<Reference> iterator() { return references.iterator(); } void add(Reference reference, NodeTraversal t, Var v) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means that we don't * have enough information to make a

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> > 0 && references.get(0).isInitializingDeclaration()) { return true; } return false; } } /** * Represents a single declaration or reference to a variable. */ static final class Reference implements StaticReference<JSType> { private static final Set<Integer> DECLARATION_PARENTS = ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH); private final Node nameNode; private final BasicBlock basicBlock; private final Scope scope; private final InputId inputId; private final StaticSourceFile sourceFile; Reference(Node nameNode, NodeTraversal t, BasicBlock basicBlock) { this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId()); } // Bleeding functions are weird, because the declaration does // not appear inside their scope. So they need their own constructor. static Reference newBleedingFunction(NodeTraversal t, BasicBlock basicBlock, Node func) { return new Reference(func.getFirstChild(), basicBlock, t.getScope(), t.getInput().getInputId()); } /** * Creates a variable reference in a given script file name, used in tests. * * @return The created reference. */ @VisibleForTesting static Reference createRefForTest(CompilerInput input) { return new Reference(new Node(Token.NAME), null, null, input.getInputId()); } private Reference(Node nameNode, BasicBlock basicBlock, Scope scope, InputId inputId) { this.nameNode = nameNode; this.basicBlock = basicBlock; this.scope = scope; this.inputId = inputId; this.sourceFile = nameNode.getStaticSourceFile(); } /** * Makes a copy of the current reference using a new Scope instance. */ Reference cloneWithNewScope(Scope newScope) { return new Reference(nameNode, basicBlock, newScope, inputId); } @Override public Var getSymbol() { return scope.getVar(nameNode.getString()); } @Override public Node getNode() { return nameNode; } public InputId getInputId() { return inputId; } @Override public StaticSourceFile getSourceFile() { return sourceFile; } boolean isDeclaration() { Node parent = getParent(); Node grandparent = parent.getParent(); return DECLARATION_PARENTS.contains(parent.getType())

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> || parent.isParamList() && grandparent.isFunction(); } boolean isVarDeclaration() { return getParent().isVar(); } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(getParent()); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return isDeclaration() && !getParent().isVar() || nameNode.getFirstChild() != null; } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { Node parent = getParent(); return (parent.isFunction()) ? parent : NodeUtil.getAssignedValue(nameNode); } BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /**

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> * Whether this block denotes a function scope. */ private final boolean isFunction; /** * Whether this block denotes a loop. */ private final boolean isLoop; /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); this.isFunction = root.isFunction(); if (root.getParent() != null) { int pType = root.getParent().getType(); this.isLoop = pType == Token.DO || pType == Token.WHILE || pType == Token.FOR; } else { this.isLoop = false; } } BasicBlock getParent() { return parent; } /** * Determines whether this block is equivalent to the very first block that * is created when reference collection traversal enters global scope. Note * that when traversing a single script in a hot-swap fashion a new instance * of {@code BasicBlock} is created. * * @return true if this is global scope block. */ boolean isGlobalScopeBlock() { return getParent() == null; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If thatBlock is a descendant of this block, and there are no hoisted // blocks between them, then this block must start before thatBlock. BasicBlock currentBlock; for (currentBlock = thatBlock; currentBlock != null && currentBlock != this; currentBlock = currentBlock.getParent()) { if (currentBlock.isHoisted) { return false; } } if (currentBlock == this) { return true; } if (isGlobalScopeBlock() && thatBlock.isGlobalScopeBlock()) { return true; } return false; } } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Creator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { if (enterSection) { noTypeCheckSection++; } else { noTypeCheckSection--; } } validator.setShouldReport(noTypeCheckSection == 0); break; } } private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, String... arguments) { if (noTypeCheckSection == 0) { t.report(n, diagnosticType, arguments); } } @Override public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { checkNoTypeCheckSection(n, true); switch (n.getType()) { case Token.FUNCTION: // normal type checking final Scope outerScope = t.getScope(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_MASKS

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.PARAM_LIST: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (!parent.isFunction()) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: ensureTyped(t, n, NUMBER_TYPE); break; case Token.STRING: ensureTyped(t, n, STRING_TYPE); break; case Token.STRING_KEY: typeable = false; break; case Token.GETTER_DEF: case Token.SETTER_DEF: // Object literal keys are handled with OBJECTLIT break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.REGEXP:

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.isAssign() && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !parent.isExprResult(); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID: ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild()); if (!childType.matchesInt32Context()) { report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = left

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>TypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.isNE()) { result = result.not(); } report(t, n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), rightType.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotNullOrUndefined( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotNullOrUndefined( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); leftType = get

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>JSType(left); rightType = getJSType(right); validator.expectObject(t, n, rightType, "'in' requires an object"); validator.expectString(t, left, leftType, "left side of 'in'"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, leftType, "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.ASSIGN: visitAssign(t, n); typeable = false; break; case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject( t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT_CASE: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.isObjectLit()) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } if (n.isObjectLit()) { for (Node key : n.children()) { visitObjLitKey(t, key, n); } } break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } checkNoTypeCheckSection(n, false); } /** * Counts the given node in the typed statistics. * @param n a node that should be typed */ private void doPercentTypedAccounting(NodeTraversal t, Node n) { JSType type = n.getJSType(); if (type == null) { nullCount++; } else if (type.isUnknownType()) { if (reportUnknownTypes.isOn()) { compiler.report( t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } unknownCount++; } else { typedCount++; } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore. Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.PARAM_LIST || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.isAssign()) { return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; case Token.NOT: return parent.getParent().isOr() && parent.getParent().getFirstChild() == parent; } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JS

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Node function = t.getEnclosingFunction(); // This is a misplaced return, but the real JS will fail to compile, // so let it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { report(t, left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { report(t, right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); } break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD: case Token.ADD: break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(op)); } ensureTyped(t, n); } /** * <p>Checks enum aliases. * * <p>We verify that the enum element type of the enum used * for initialization is a subtype of the enum element type of * the enum the value is being copied in.</p> * * <p>Example:</p> * <pre>var myEnum = myOtherEnum;</pre> * * <p>Enum aliases are irregular, so we need special code for this :(</p> * * @param value the value used for initialization of the enum */ private void checkEnumAlias( NodeTraversal t, JSDocInfo declInfo, Node value) { if (declInfo == null || !declInfo.hasEnumParameterType()) { return; } JSType valueType = getJSType(value); if (!valueType.isEnumType()) { return; } EnumType valueEnumType = valueType.toMaybeEnumType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, declInfo.getEnumParameterType().evaluate(t.getScope(), typeRegistry), "incompatible enum element types"); } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(n

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.regex.RegExpTree; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable, and checks that regular expressions * are syntactically valid. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( "JSC_MALFORMED_REGEXP", "Malformed Regular Expression: {0}"); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW &&

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !first))) { t.report(n, REGEXP_REFERENCE); globalRegExpPropertiesUsed = true; } } // Check the syntax of regular expression patterns. } else if (n.isRegExp()) { String pattern = n.getFirstChild().getString(); String flags = n.getChildCount() == 2 ? n.getLastChild().getString() : ""; try { RegExpTree.parseRegExp(pattern, flags); } catch (IllegalArgumentException ex) { t.report(n, MALFORMED_REGEXP, ex.getMessage()); } } } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>StaticSourceFile; import java.util.Set; /** * IRFactory transforms the new AST to the old AST. * */ class IRFactory { static final String GETTER_ERROR_MESSAGE = "getters are not supported in older versions of JS. " + "If you are targeting newer versions of JS, " + "set the appropriate language_in option."; static final String SETTER_ERROR_MESSAGE = "setters are not supported in older versions of JS. " + "If you are targeting newer versions of JS, " + "set the appropriate language_in option."; static final String SUSPICIOUS_COMMENT_WARNING = "Non-JSDoc comment has annotations. " + "Did you mean to start it with '/**'?"; private final String sourceString; private final StaticSourceFile sourceFile; private final String sourceName; private final Config config; private final ErrorReporter errorReporter; private final TransformDispatcher transformDispatcher; private static final ImmutableSet<String> ALLOWED_DIRECTIVES = ImmutableSet.of("use strict"); private static final ImmutableSet<String> ES5_RESERVED_KEYWORDS = ImmutableSet.of( // From Section 7.6.1.2 "class", "const", "enum", "export", "extends", "import", "super"); private static final ImmutableSet<String> ES5_STRICT_RESERVED_KEYWORDS = ImmutableSet.of( // From Section 7.6.1.2 "class", "const", "enum", "export", "extends", "import", "super", "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"); private final Set<String> reservedKeywords; private final Set<Comment> parsedComments = Sets.newHashSet(); // @license text gets appended onto the fileLevelJsDocBuilder as found, // and stored in JSDocInfo for placeholder node. Node rootNodeJsDocHolder = new Node(Token.SCRIPT); Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = rootNodeJsDocHolder.getJsDocBuilderForNode(); JSDocInfo fileOverviewInfo = null; // Use a template node for properties set on all nodes to minimize the // memory footprint associated with these. private Node templateNode; // TODO(johnlenz): Consider creating a template pool for ORIGINALNAME_PROP. private IRFactory(String

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> sourceString, StaticSourceFile sourceFile, Config config, ErrorReporter errorReporter) { this.sourceString = sourceString; this.sourceFile = sourceFile; // Sometimes this will be null in tests. this.sourceName = sourceFile == null ? null : sourceFile.getName(); this.config = config; this.errorReporter = errorReporter; this.transformDispatcher = new TransformDispatcher(); // The template node properties are applied to all nodes in this transform. this.templateNode = createTemplateNode(); switch (config.languageMode) { case ECMASCRIPT3: // Reserved words are handled by the Rhino parser. reservedKeywords = null; break; case ECMASCRIPT5: reservedKeywords = ES5_RESERVED_KEYWORDS; break; case ECMASCRIPT5_STRICT: reservedKeywords = ES5_STRICT_RESERVED_KEYWORDS; break; default: throw new IllegalStateException("unknown language mode"); } } // Create a template node to use as a source of common attributes, this allows // the prop structure to be shared among all the node from this source file. // This reduces the cost of these properties to O(nodes) to O(files). private Node createTemplateNode() { // The Node type choice is arbitrary. Node templateNode = new Node(Token.SCRIPT); templateNode.setStaticSourceFile(sourceFile); return templateNode; } public static Node transformTree(AstRoot node, StaticSourceFile sourceFile, String sourceString, Config config, ErrorReporter errorReporter) { IRFactory irFactory = new IRFactory(sourceString, sourceFile, config, errorReporter); Node irNode = irFactory.transform(node); if (node.getComments() != null) { for (Comment comment : node.getComments()) { if (comment.getCommentType() == CommentType.JSDOC && !irFactory.parsedComments.contains(comment)) { irFactory.handlePossibleFileOverviewJsDoc(comment, irNode); } else if (comment.getCommentType() == CommentType.BLOCK_COMMENT) { irFactory.handleBlockComment(comment); } } } irFactory.setFileOverviewJsDoc(irNode); return irNode; } private void setFileOverviewJsDoc(Node irNode) { // Only after we've seen all @fileoverview entries, attach the // last one to the root node, and

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> copy the found license strings // to that node. JSDocInfo rootNodeJsDoc = rootNodeJsDocHolder.getJSDocInfo(); if (rootNodeJsDoc != null) { irNode.setJSDocInfo(rootNodeJsDoc); rootNodeJsDoc.setAssociatedNode(irNode); } if (fileOverviewInfo != null) { if ((irNode.getJSDocInfo() != null) && (irNode.getJSDocInfo().getLicense() != null)) { fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); fileOverviewInfo.setAssociatedNode(irNode); } } private Node transformBlock(AstNode node) { Node irNode = transform(node); if (!irNode.isBlock()) { if (irNode.isEmpty()) { irNode.setType(Token.BLOCK); irNode.setWasEmptyNode(true); } else { Node newBlock = newNode(Token.BLOCK, irNode); newBlock.setLineno(irNode.getLineno()); newBlock.setCharno(irNode.getCharno()); maybeSetLengthFrom(newBlock, node); irNode = newBlock; } } return irNode; } /** * Check to see if the given block comment looks like it should be JSDoc. */ private void handleBlockComment(Comment comment) { String value = comment.getValue(); if (value.indexOf("/* @") != -1 || value.indexOf("\n * @") != -1) { errorReporter.warning( SUSPICIOUS_COMMENT_WARNING, sourceName, comment.getLineno(), "", 0); } } /** * @return true if the jsDocParser represents a fileoverview. */ private boolean handlePossibleFileOverviewJsDoc( JsDocInfoParser jsDocParser) { if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) { fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo(); return true; } return false; } private void handlePossibleFileOverviewJsDoc(Comment comment, Node irNode) { JsDocInfoParser jsDocParser = createJsDocInfoParser(comment, irNode); parsedComments.add(comment); handlePossibleFileOverviewJsDoc(

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret; if (n instanceof Name) { ret = transformNameAsString((Name)n); } else if (n instanceof NumberLiteral) { ret = transformNumberAsString((NumberLiteral)n); ret.putBooleanProp(Node.QUOTED_PROP, true); } else { ret = transform(n); ret.putBooleanProp(Node.QUOTED_PROP, true); } Preconditions.checkState(ret.isString()); return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.ARRAYLIT); for (AstNode child : literalNode.getElements()) { Node c = transform(child); node.addChildToBack(c); } return node; } @Override Node processAssignment(Assignment assignmentNode) { Node assign = processInfixExpression(assignmentNode); Node target = assign.getFirstChild(); if (!validAssignmentTarget(target)) { errorReporter.error( "invalid assignment target", sourceName, target.getLineno(), "", 0); } return assign; } @Override Node processAstRoot(AstRoot rootNode) { Node node = newNode(Token.SCRIPT); for (com.google.javascript.rhino.head.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * ECMA-262, Edition 5. * * It would be nice if Rhino would eventually take care of

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return nType == Token.EXPR_RESULT && n.getFirstChild().isString() && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = newNode(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = newNode(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { errorReporter.error( "Catch clauses are not supported", sourceName, clauseNode.getCatchCondition().getLineno(), "", 0); } node.addChildToBack(transformBlock(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return newNode( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = newNode(Token.CONTINUE); if (statementNode.getLabel() != null) { Node labelName = transform(statementNode.getLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>NAME); node.addChildToBack(labelName); } return node; } @Override Node processDoLoop(DoLoop loopNode) { return newNode( Token.DO, transformBlock(loopNode.getBody()), transform(loopNode.getCondition())); } @Override Node processElementGet(ElementGet getNode) { return newNode( Token.GETELEM, transform(getNode.getTarget()), transform(getNode.getElement())); } @Override Node processEmptyExpression(EmptyExpression exprNode) { Node node = newNode(Token.EMPTY); return node; } @Override Node processEmptyStatement(EmptyStatement exprNode) { Node node = newNode(Token.EMPTY); return node; } @Override Node processExpressionStatement(ExpressionStatement statementNode) { Node node = newNode(transformTokenType(statementNode.getType())); node.addChildToBack(transform(statementNode.getExpression())); return node; } @Override Node processForInLoop(ForInLoop loopNode) { if (loopNode.isForEach()) { errorReporter.error( "unsupported language extension: for each", sourceName, loopNode.getLineno(), "", 0); // Return the bare minimum to put the AST in a valid state. return newNode(Token.EXPR_RESULT, Node.newNumber(0)); } return newNode( Token.FOR, transform(loopNode.getIterator()), transform(loopNode.getIteratedObject()), transformBlock(loopNode.getBody())); } @Override Node processForLoop(ForLoop loopNode) { Node node = newNode( Token.FOR, transform(loopNode.getInitializer()), transform(loopNode.getCondition()), transform(loopNode.getIncrement())); node.addChildToBack(transformBlock(loopNode.getBody())); return node; } @Override Node processFunctionCall(FunctionCall callNode) { Node node = newNode(transformTokenType(callNode.getType()), transform(callNode.getTarget())); for (AstNode child : callNode.getArguments()) { node.addChildToBack(transform(child)); } node.setLineno(node.getFirstChild().getLineno()); node.setCharno(node.getFirstChild().getCharno()); maybeSetLengthFrom(node, callNode); return node; } @Override Node processFunctionNode(FunctionNode functionNode) { Name name

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> = functionNode.getFunctionName(); Boolean isUnnamedFunction = false; if (name == null) { int functionType = functionNode.getFunctionType(); if (functionType != FunctionNode.FUNCTION_EXPRESSION) { errorReporter.error( "unnamed function statement", sourceName, functionNode.getLineno(), "", 0); // Return the bare minimum to put the AST in a valid state. return newNode(Token.EXPR_RESULT, Node.newNumber(0)); } name = new Name(); name.setIdentifier(""); isUnnamedFunction = true; } Node node = newNode(Token.FUNCTION); Node newName = transform(name); if (isUnnamedFunction) { // Old Rhino tagged the empty name node with the line number of the // declaration. newName.setLineno(functionNode.getLineno()); // TODO(bowdidge) Mark line number of paren correctly. // Same problem as below - the left paren might not be on the // same line as the function keyword. int lpColumn = functionNode.getAbsolutePosition() + functionNode.getLp(); newName.setCharno(position2charno(lpColumn)); maybeSetLengthFrom(newName, name); } node.addChildToBack(newName); Node lp = newNode(Token.PARAM_LIST); // The left paren's complicated because it's not represented by an // AstNode, so there's nothing that has the actual line number that it // appeared on. We know the paren has to appear on the same line as the // function name (or else a semicolon will be inserted.) If there's no // function name, assume the paren was on the same line as the function. // TODO(bowdidge): Mark line number of paren correctly. Name fnName = functionNode.getFunctionName(); if (fnName != null) { lp.setLineno(fnName.getLineno()); } else { lp.setLineno(functionNode.getLineno()); } int lparenCharno = functionNode.getLp() + functionNode.getAbsolutePosition(); lp.setCharno(position2charno(lparenCharno)); for (AstNode param : functionNode.getParams()) { Node paramNode = transform(param); // When in ideMode Rhino can generate a param list with only a single // ErrorNode. This is transformed into an EMPTY

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> node. Drop this node in // ideMode to keep the AST in a valid state. if (paramNode.isName()) { lp.addChildToBack(paramNode); } else { // We expect this in ideMode or when there is an error handling // destructuring parameter assignments which aren't supported // (an error has already been reported). Preconditions.checkState( config.isIdeMode || paramNode.isObjectLit() || paramNode.isArrayLit()); } } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); if (!bodyNode.isBlock()) { // When in ideMode Rhino tries to parse some constructs the compiler // doesn't support, repair it here. see Rhino's // Parser#parseFunctionBodyExpr. Preconditions.checkState(config.isIdeMode); bodyNode = IR.block(); } parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = newNode(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transformBlock(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transformBlock(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); n.setLineno(exprNode.getLineno()); n.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(n, exprNode); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return newNode(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return newStringNode(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = newNode(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); maybeSetLengthFrom(cur, label); int clauseAbsolutePosition = position2charno(label.getAbsolutePosition()); cur.setCharno(clauseAbsolutePosition); prev = cur; cur = newNode(Token.LABEL); } prev.addChildToBack(transform(statementNode.getStatement())); return node; } @Override Node processName(Name nameNode) { return processName(nameNode, false); } Node processName(Name nameNode, boolean asString) { if (asString) { return newStringNode(Token.STRING, nameNode.getIdentifier()); } else { if (isReservedKeyword(nameNode.getIdentifier())) { errorReporter.error( "identifier is a reserved word", sourceName, nameNode.getLineno(), "", 0); } return newStringNode(Token.NAME, nameNode.getIdentifier()); } } /** * @return Whether the */ private boolean isReservedKeyword(String identifier) { return reservedKeywords != null && reservedKeywords.contains(identifier); } @Override Node processNewExpression(NewExpression exprNode) { return processFunctionCall(exprNode); } @Override Node processNumberLiteral(NumberLiteral literalNode) { return newNumberNode(literalNode.getNumber()); } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = newNode(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (config.languageMode == LanguageMode.ECMASCRIPT3) { if (el.isGetter()) { reportGetter(el); continue; } else if (el.isSetter()) { reportSetter(el); continue; } } Node key = transformAsString(el.getLeft()); key.setType(Token.STRING_KEY); Node value = transform(el.getRight()); if (el.isGetter()) { key.setType(Token.GETTER_DEF); Preconditions.checkState(value.isFunction()); if (getFnParamNode(value).hasChildren()) { reportGetterParam(el.getLeft()); } } else if (el.isSetter()) { key.setType(Token.SETTER

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>_DEF); Preconditions.checkState(value.isFunction()); if (!getFnParamNode(value).hasOneChild()) { reportSetterParam(el.getLeft()); } } key.addChildToFront(value); node.addChildToBack(key); } return node; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ Node getFnParamNode(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } @Override Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { Node leftChild = transform(getNode.getTarget()); Node newNode = newNode( Token.GETPROP, leftChild, transformAsString(getNode.getProperty())); newNode.setLineno(leftChild.getLineno()); newNode.setCharno(leftChild.getCharno()); maybeSetLengthFrom(newNode, getNode); return newNode; } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = newStringNode(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(literalStringNode, literalNode); Node node = newNode(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = newStringNode(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); maybeSetLengthFrom(flagsNode, literalNode); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = newNode(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { String value = literalNode.getValue(); Node n = newStringNode(value); if (value.indexOf('\u000B') != -1) { // NOTE(nicksantos): In JavaScript, there are 3 ways to // represent a vertical tab: \v, \x0B, \u000B. // The \v notation was added later, and is not understood // on IE. So we need to preserve it as-is. This is really // obnoxious, because we do not have a good way to represent // how the original string was encoded without making the // representation of strings much more complicated. // // To handle this, we look at the original source test, and // mark the string as \v-encoded or not. If a string is // \v encoded, then all the vertical tabs in that string // will be encoded with a \v. int start = literalNode.getAbsolutePosition(); int end = start + literalNode.getLength(); if (start < sourceString.length() && (sourceString.substring( start, Math.min(sourceString.length(), end)) .indexOf("\\v") != -1)) { n.putBooleanProp(Node.SLASH_V, true); } } return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = newNode(Token.DEFAULT_CASE); } else { AstNode expr = caseNode.getExpression(); node = newNode(Token.CASE, transform(expr)); } Node block = newNode(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.setCharno(position2charno(caseNode.getAbsolutePosition())); maybeSetLengthFrom(block, caseNode); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> = newNode(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return newNode(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = newNode(Token.TRY, transformBlock(statementNode.getTryBlock())); Node block = newNode(Token.BLOCK); boolean lineSet = false; for (CatchClause cc : statementNode.getCatchClauses()) { // Mark the enclosing block at the same line as the first catch // clause. if (lineSet == false) { block.setLineno(cc.getLineno()); maybeSetLengthFrom(block, cc); lineSet = true; } block.addChildToBack(transform(cc)); } node.addChildToBack(block); AstNode finallyBlock = statementNode.getFinallyBlock(); if (finallyBlock != null) { node.addChildToBack(transformBlock(finallyBlock)); } // If we didn't set the line on the catch clause, then // we've got an empty catch clause. Set its line to be the same // as the finally block (to match Old Rhino's behavior.) if ((lineSet == false) && (finallyBlock != null)) { block.setLineno(finallyBlock.getLineno()); maybeSetLengthFrom(block, finallyBlock); } return node; } @Override Node processUnaryExpression(UnaryExpression exprNode) { int type = transformTokenType(exprNode.getType()); Node operand = transform(exprNode.getOperand()); if (type == Token.NEG && operand.isNumber()) { operand.setDouble(-operand.getDouble()); return operand; } else { if (type == Token.DELPROP && !(operand.isGetProp() || operand.isGetElem() || operand.isName())) { String msg = "Invalid delete operand. Only properties can be deleted."; errorReporter.error( msg, sourceName, operand.getLineno(), "", 0); } else if (type == Token.INC || type == Token.DEC) { if (!validAssignmentTarget(operand)) { String msg = (type == Token.INC)

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> ? "invalid increment target" : "invalid decrement target"; errorReporter.error( msg, sourceName, operand.getLineno(), "", 0); } } Node node = newNode(type, operand); if (exprNode.isPostfix()) { node.putBooleanProp(Node.INCRDECR_PROP, true); } return node; } } private boolean validAssignmentTarget(Node target) { switch (target.getType()) { case Token.NAME: case Token.GETPROP: case Token.GETELEM: return true; } return false; } @Override Node processVariableDeclaration(VariableDeclaration declarationNode) { if (!config.acceptConstKeyword && declarationNode.getType() == com.google.javascript.rhino.head.Token.CONST) { processIllegalToken(declarationNode); } Node node = newNode(Token.VAR); for (VariableInitializer child : declarationNode.getVariables()) { node.addChildToBack(transform(child)); } return node; } @Override Node processVariableInitializer(VariableInitializer initializerNode) { Node node = transform(initializerNode.getTarget()); if (initializerNode.getInitializer() != null) { Node initalizer = transform(initializerNode.getInitializer()); node.addChildToBack(initalizer); } return node; } @Override Node processWhileLoop(WhileLoop loopNode) { return newNode( Token.WHILE, transform(loopNode.getCondition()), transformBlock(loopNode.getBody())); } @Override Node processWithStatement(WithStatement statementNode) { return newNode( Token.WITH, transform(statementNode.getExpression()), transformBlock(statementNode.getStatement())); } @Override Node processIllegalToken(AstNode node) { errorReporter.error( "Unsupported syntax: " + com.google.javascript.rhino.head.Token.typeToName( node.getType()), sourceName, node.getLineno(), "", 0); return newNode(Token.EMPTY); } void reportDestructuringAssign(AstNode node) { errorReporter.error( "destructuring assignment forbidden", sourceName, node.getLineno(), "", 0); } void reportGetter(AstNode node) { errorReporter.error( GETTER_ERROR_MESSAGE, sourceName, node.get

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Lineno(), "", 0); } void reportSetter(AstNode node) { errorReporter.error( SETTER_ERROR_MESSAGE, sourceName, node.getLineno(), "", 0); } void reportGetterParam(AstNode node) { errorReporter.error( "getters may not have parameters", sourceName, node.getLineno(), "", 0); } void reportSetterParam(AstNode node) { errorReporter.error( "setters must have exactly one parameter", sourceName, node.getLineno(), "", 0); } } private static int transformTokenType(int token) { switch (token) { case com.google.javascript.rhino.head.Token.RETURN: return Token.RETURN; case com.google.javascript.rhino.head.Token.BITOR: return Token.BITOR; case com.google.javascript.rhino.head.Token.BITXOR: return Token.BITXOR; case com.google.javascript.rhino.head.Token.BITAND: return Token.BITAND; case com.google.javascript.rhino.head.Token.EQ: return Token.EQ; case com.google.javascript.rhino.head.Token.NE: return Token.NE; case com.google.javascript.rhino.head.Token.LT: return Token.LT; case com.google.javascript.rhino.head.Token.LE: return Token.LE; case com.google.javascript.rhino.head.Token.GT: return Token.GT; case com.google.javascript.rhino.head.Token.GE: return Token.GE; case com.google.javascript.rhino.head.Token.LSH: return Token.LSH; case com.google.javascript.rhino.head.Token.RSH: return Token.RSH; case com.google.javascript.rhino.head.Token.URSH: return Token.URSH; case com.google.javascript.rhino.head.Token.ADD: return Token.ADD; case com.google.javascript.rhino.head.Token.SUB: return Token.SUB; case com.google.javascript.rhino.head.Token.MUL: return Token.MUL; case com.google.javascript.rhino.head.Token.DIV:

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> return Token.DIV; case com.google.javascript.rhino.head.Token.MOD: return Token.MOD; case com.google.javascript.rhino.head.Token.NOT: return Token.NOT; case com.google.javascript.rhino.head.Token.BITNOT: return Token.BITNOT; case com.google.javascript.rhino.head.Token.POS: return Token.POS; case com.google.javascript.rhino.head.Token.NEG: return Token.NEG; case com.google.javascript.rhino.head.Token.NEW: return Token.NEW; case com.google.javascript.rhino.head.Token.DELPROP: return Token.DELPROP; case com.google.javascript.rhino.head.Token.TYPEOF: return Token.TYPEOF; case com.google.javascript.rhino.head.Token.GETPROP: return Token.GETPROP; case com.google.javascript.rhino.head.Token.GETELEM: return Token.GETELEM; case com.google.javascript.rhino.head.Token.CALL: return Token.CALL; case com.google.javascript.rhino.head.Token.NAME: return Token.NAME; case com.google.javascript.rhino.head.Token.NUMBER: return Token.NUMBER; case com.google.javascript.rhino.head.Token.STRING: return Token.STRING; case com.google.javascript.rhino.head.Token.NULL: return Token.NULL; case com.google.javascript.rhino.head.Token.THIS: return Token.THIS; case com.google.javascript.rhino.head.Token.FALSE: return Token.FALSE; case com.google.javascript.rhino.head.Token.TRUE: return Token.TRUE; case com.google.javascript.rhino.head.Token.SHEQ: return Token.SHEQ; case com.google.javascript.rhino.head.Token.SHNE: return Token.SHNE; case com.google.javascript.rhino.head.Token.REGEXP: return Token.REGEXP; case com.google.javascript.rhino.head.Token.THROW: return Token.THROW; case com.google.javascript.rhino.head.Token.IN: return Token.IN; case

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> com.google.javascript.rhino.head.Token.INSTANCEOF: return Token.INSTANCEOF; case com.google.javascript.rhino.head.Token.ARRAYLIT: return Token.ARRAYLIT; case com.google.javascript.rhino.head.Token.OBJECTLIT: return Token.OBJECTLIT; case com.google.javascript.rhino.head.Token.TRY: return Token.TRY; // The LP represents a parameter list case com.google.javascript.rhino.head.Token.LP: return Token.PARAM_LIST; case com.google.javascript.rhino.head.Token.COMMA: return Token.COMMA; case com.google.javascript.rhino.head.Token.ASSIGN: return Token.ASSIGN; case com.google.javascript.rhino.head.Token.ASSIGN_BITOR: return Token.ASSIGN_BITOR; case com.google.javascript.rhino.head.Token.ASSIGN_BITXOR: return Token.ASSIGN_BITXOR; case com.google.javascript.rhino.head.Token.ASSIGN_BITAND: return Token.ASSIGN_BITAND; case com.google.javascript.rhino.head.Token.ASSIGN_LSH: return Token.ASSIGN_LSH; case com.google.javascript.rhino.head.Token.ASSIGN_RSH: return Token.ASSIGN_RSH; case com.google.javascript.rhino.head.Token.ASSIGN_URSH: return Token.ASSIGN_URSH; case com.google.javascript.rhino.head.Token.ASSIGN_ADD: return Token.ASSIGN_ADD; case com.google.javascript.rhino.head.Token.ASSIGN_SUB: return Token.ASSIGN_SUB; case com.google.javascript.rhino.head.Token.ASSIGN_MUL: return Token.ASSIGN_MUL; case com.google.javascript.rhino.head.Token.ASSIGN_DIV: return Token.ASSIGN_DIV; case com.google.javascript.rhino.head.Token.ASSIGN_MOD: return Token.ASSIGN_MOD; case com.google.javascript.rhino.head.Token.HOOK: return Token.HOOK; case com.google.javascript.rhino.head.Token.OR: return Token.OR; case com.google.javascript.rhino.head.Token.AND: return Token.AND;

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case com.google.javascript.rhino.head.Token.INC: return Token.INC; case com.google.javascript.rhino.head.Token.DEC: return Token.DEC; case com.google.javascript.rhino.head.Token.FUNCTION: return Token.FUNCTION; case com.google.javascript.rhino.head.Token.IF: return Token.IF; case com.google.javascript.rhino.head.Token.SWITCH: return Token.SWITCH; case com.google.javascript.rhino.head.Token.CASE: return Token.CASE; case com.google.javascript.rhino.head.Token.DEFAULT: return Token.DEFAULT_CASE; case com.google.javascript.rhino.head.Token.WHILE: return Token.WHILE; case com.google.javascript.rhino.head.Token.DO: return Token.DO; case com.google.javascript.rhino.head.Token.FOR: return Token.FOR; case com.google.javascript.rhino.head.Token.BREAK: return Token.BREAK; case com.google.javascript.rhino.head.Token.CONTINUE: return Token.CONTINUE; case com.google.javascript.rhino.head.Token.VAR: return Token.VAR; case com.google.javascript.rhino.head.Token.WITH: return Token.WITH; case com.google.javascript.rhino.head.Token.CATCH: return Token.CATCH; case com.google.javascript.rhino.head.Token.VOID: return Token.VOID; case com.google.javascript.rhino.head.Token.EMPTY: return Token.EMPTY; case com.google.javascript.rhino.head.Token.BLOCK: return Token.BLOCK; case com.google.javascript.rhino.head.Token.LABEL: return Token.LABEL; case com.google.javascript.rhino.head.Token.EXPR_VOID: case com.google.javascript.rhino.head.Token.EXPR_RESULT: return Token.EXPR_RESULT; case com.google.javascript.rhino.head.Token.SCRIPT: return Token.SCRIPT; case com.google.javascript.rhino.head.Token.GET: return Token.GETTER_DEF; case com.google.javascript.rhino.head.Token.SET:

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> return Token.SETTER_DEF; case com.google.javascript.rhino.head.Token.CONST: return Token.CONST; case com.google.javascript.rhino.head.Token.DEBUGGER: return Token.DEBUGGER; } // Token without name throw new IllegalStateException(String.valueOf(token)); } // Simple helper to create nodes and set the initial node properties. private Node newNode(int type) { return new Node(type).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1) { return new Node(type, child1).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1, Node child2) { return new Node(type, child1, child2).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1, Node child2, Node child3) { return new Node(type, child1, child2, child3).clonePropsFrom(templateNode); } private Node newStringNode(String value) { return IR.string(value).clonePropsFrom(templateNode); } private Node newStringNode(int type, String value) { return Node.newString(type, value).clonePropsFrom(templateNode); } private Node newNumberNode(Double value) { return IR.number(value).clonePropsFrom(templateNode); } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> { if (synthesizedExternsInput == null) { synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS); } return synthesizedExternsInput; } @Override public double getProgress() { return progress; } @Override void setProgress(double newProgress) { if (newProgress > 1.0) { progress = 1.0; } else if (newProgress < 0.0) { progress = 0.0; } else { progress = newProgress; } } /** * Replaces one file in a hot-swap mode. The given JsAst should be made * from a new version of a file that already was present in the last compile * call. If the file is new, this will silently ignored. * * @param ast the ast of the file that is being replaced */ public void replaceScript(JsAst ast) { CompilerInput input = this.getInput(ast.getInputId()); if (!replaceIncrementalSourceAst(ast)) { return; } Node originalRoot = input.getAstRoot(this); processNewScript(ast, originalRoot); } /** * Adds a new Script AST to the compile state. If a script for the same file * already exists the script will not be added, instead a call to * #replaceScript should be used. * * @param ast the ast of the new file */ public void addNewScript(JsAst ast) { if (!addNewSourceAst(ast)) { return; } Node emptyScript = new Node(Token.SCRIPT); InputId inputId = ast.getInputId(); emptyScript.setInputId(inputId); emptyScript.setStaticSourceFile( SourceFile.fromCode(inputId.getIdName(), "")); processNewScript(ast, emptyScript); } private void processNewScript(JsAst ast, Node originalRoot) { Node js = ast.getAstRoot(this); Preconditions.checkNotNull(js); runHotSwap(originalRoot, js, this.getCleanupPassConfig()); // NOTE: If hot swap passes that use GlobalNamespace are added, we will need // to revisit this approach to clearing GlobalNamespaces runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks); this.getTypeRegistry().clearNamedTypes(); this.removeSyntheticVarsInput(); runHotSwap(originalRoot,

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * This class walks the AST and validates that the structure is correct. * * @author johnlenz@google.com (John Lenz) */ public class AstValidator implements CompilerPass { // Possible enhancements: // * verify NAME, LABEL_NAME, GETPROP property name and unquoted // object-literal keys are valid JavaScript identifiers. // * optionally verify every node has source location information. // * optionally verify every node has an assigned JSType // public interface ViolationHandler { void handleViolation(String message, Node n); } private final ViolationHandler violationHandler; public AstValidator(ViolationHandler handler) { this.violationHandler = handler; } public AstValidator() { this.violationHandler = new ViolationHandler() { @Override public void handleViolation(String message, Node n) { throw new IllegalStateException( message + " Reference node " + n.toString()); } }; } @Override public void process(Node externs, Node root) { if (externs != null) { validateCodeRoot(externs); } if (root != null) { validateCodeRoot(root); } } public void validateRoot(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); validateChildCount(n, 2); validateCodeRoot(n.getFirstChild()); validateCodeRoot(n.getLastChild()); } public void validateCodeRoot(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateScript(c); } } public void validateScript(Node n) { validateNodeType(Token.SCRIPT, n); validateIsSynthetic(n); validateHasSourceName(n); validateHasInputId(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } public void validateStatement(Node n) { switch (n.getType()) { case Token.LABEL: validateLabel(n); return; case Token.BLOCK: validateBlock(n); return; case Token.FUNCTION: validateFunctionStatement(n); return; case Token.WITH: validateWith(n); return; case Token.FOR: validateFor(n); return; case Token.WHILE: validateWhile(n); return; case Token.DO: validateDo(n); return; case Token.SWITCH: validateSwitch(n); return; case Token.IF: validateIf(n); return; case Token.VAR: validateVar(n); return; case Token.EXPR_RESULT: validateExprStmt(n); return; case Token.RETURN: validateReturn(n); return; case Token.THROW: validateThrow(n); return; case Token.TRY: validateTry(n); return; case Token.BREAK: validateBreak(n); return; case Token.CONTINUE: validateContinue(n); return; case Token.EMPTY: validateChildless(n); return; case Token.DEBUGGER: validateChildless(n); return; default: violation("Expected statement but was " + Token.name(n.getType()) + ".", n); } } public void validateExpression(Node n) { switch (n.getType()) { // Childless expressions case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: validateChildless(n); return; // General unary ops case Token.DELPROP: case Token.POS: case Token.NEG: case Token.NOT: case Token.INC: case Token.DEC: case Token.TYPEOF: case Token.VOID: case Token.BIT

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>NOT: validateUnaryOp(n); return; // General binary ops case Token.COMMA: case Token.OR: case Token.AND: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.ADD: case Token.MUL: case Token.MOD: case Token.DIV: validateBinaryOp(n); return; // Assignments case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: validateAssignmentExpression(n); return; case Token.HOOK: validateTrinaryOp(n); return; // Node types that require special handling case Token.STRING: validateString(n); return; case Token.NUMBER: validateNumber(n); return; case Token.NAME: validateName(n); return; case Token.GETELEM: validateBinaryOp(n); return; case Token.GETPROP: validateGetProp(n); return; case Token.ARRAYLIT: validateArrayLit(n); return; case Token.OBJECTLIT: validateObjectLit(n); return; case Token.REGEXP: validateRegExpLit(n); return; case Token.CALL: validateCall(n); return; case Token.NEW: validateNew(n); return; case Token.FUNCTION: validateFunctionExpression(n); return; default: violation("Expected expression but was " + Token.name(n.getType()), n); } } private void validateBlock(Node n) { validateNodeType(Token.BLOCK, n); for (Node c = n.getFirstChild();

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> c != null; c = c.getNext()) { validateStatement(c); } } private void validateSyntheticBlock(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } private void validateIsSynthetic(Node n) { if (!n.getBooleanProp(Node.SYNTHETIC_BLOCK_PROP)) { violation("Missing 'synthetic block' annotation.", n); } } private void validateHasSourceName(Node n) { String sourceName = n.getSourceFileName(); if (sourceName == null || sourceName.isEmpty()) { violation("Missing 'source name' annotation.", n); } } private void validateHasInputId(Node n) { InputId inputId = n.getInputId(); if (inputId == null) { violation("Missing 'input id' annotation.", n); } } private void validateLabel(Node n) { validateNodeType(Token.LABEL, n); validateChildCount(n, 2); validateLabelName(n.getFirstChild()); validateStatement(n.getLastChild()); } private void validateLabelName(Node n) { validateNodeType(Token.LABEL_NAME, n); validateNonEmptyString(n); validateChildCount(n, 0); } private void validateNonEmptyString(Node n) { validateNonNullString(n); if (n.getString().isEmpty()) { violation("Expected non-empty string.", n); } } private void validateNonNullString(Node n) { if (n.getString() == null) { violation("Expected non-null string.", n); } } private void validateName(Node n) { validateNodeType(Token.NAME, n); validateNonEmptyString(n); validateChildCount(n, 0); } private void validateOptionalName(Node n) { validateNodeType(Token.NAME, n); validateNonNullString(n); validateChildCount(n, 0); } private void validateFunctionStatement(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n, 3); validateName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); } private void validateFunctionExpression(

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n, 3); validateOptionalName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); } private void validateParameters(Node n) { validateNodeType(Token.PARAM_LIST, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateName(c); } } private void validateCall(Node n) { validateNodeType(Token.CALL, n); validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateExpression(c); } } private void validateNew(Node n) { validateNodeType(Token.NEW, n); validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateExpression(c); } } private void validateVar(Node n) { validateNodeType(Token.VAR, n); this.validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // Don't use the validateName here as the NAME is allowed to have // a child. validateNodeType(Token.NAME, c); validateNonEmptyString(c); validateMaximumChildCount(c, 1); if (c.hasChildren()) { validateExpression(c.getFirstChild()); } } } private void validateFor(Node n) { validateNodeType(Token.FOR, n); validateMinimumChildCount(n, 3); validateMaximumChildCount(n, 4); if (NodeUtil.isForIn(n)) { // FOR-IN validateChildCount(n, 3); validateVarOrAssignmentTarget(n.getFirstChild()); validateExpression(n.getChildAtIndex(1)); } else { // FOR validateChildCount(n, 4); validateVarOrOptionalExpression(n.getFirstChild()); validateOptionalExpression(n.getChildAtIndex(1)); validateOptionalExpression(n.getChildAtIndex(2)); } validateBlock(n.getLastChild()); } private void validateVarOrOptionalExpression(Node n) { if (n.isVar()) { validateVar(n); }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> else { validateOptionalExpression(n); } } private void validateVarOrAssignmentTarget(Node n) { if (n.isVar()) { // Only one NAME can be declared for FOR-IN expressions. this.validateChildCount(n, 1); validateVar(n); } else { validateAssignmentTarget(n); } } private void validateWith(Node n) { validateNodeType(Token.WITH, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateBlock(n.getLastChild()); } private void validateWhile(Node n) { validateNodeType(Token.WHILE, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateBlock(n.getLastChild()); } private void validateDo(Node n) { validateNodeType(Token.DO, n); validateChildCount(n, 2); validateBlock(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateIf(Node n) { validateNodeType(Token.IF, n); validateMinimumChildCount(n, 2); validateMaximumChildCount(n, 3); validateExpression(n.getFirstChild()); validateBlock(n.getChildAtIndex(1)); if (n.getChildCount() == 3) { validateBlock(n.getLastChild()); } } private void validateExprStmt(Node n) { validateNodeType(Token.EXPR_RESULT, n); validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateReturn(Node n) { validateNodeType(Token.RETURN, n); validateMaximumChildCount(n, 1); if (n.hasChildren()) { validateExpression(n.getFirstChild()); } } private void validateThrow(Node n) { validateNodeType(Token.THROW, n); validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateBreak(Node n) { validateNodeType(Token.BREAK, n); validateMaximumChildCount(n, 1); if (n.hasChildren()) { validateLabelName(n.getFirstChild()); } } private void validateContinue(Node n) { validateNodeType(Token.CONTINUE, n); validateMaximumChildCount(n, 1); if (n.hasChildren()) {

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> validateLabelName(n.getFirstChild()); } } private void validateTry(Node n) { validateNodeType(Token.TRY, n); validateMinimumChildCount(n, 2); validateMaximumChildCount(n, 3); validateBlock(n.getFirstChild()); boolean seenCatchOrFinally = false; // Validate catch Node catches = n.getChildAtIndex(1); validateNodeType(Token.BLOCK, catches); validateMaximumChildCount(catches, 1); if (catches.hasChildren()) { validateCatch(catches.getFirstChild()); seenCatchOrFinally = true; } // Validate finally if (n.getChildCount() == 3) { validateBlock(n.getLastChild()); seenCatchOrFinally = true; } if (!seenCatchOrFinally) { violation("Missing catch or finally for try statement.", n); } } private void validateCatch(Node n) { validateNodeType(Token.CATCH, n); validateChildCount(n, 2); validateName(n.getFirstChild()); validateBlock(n.getLastChild()); } private void validateSwitch(Node n) { validateNodeType(Token.SWITCH, n); validateMinimumChildCount(n, 1); validateExpression(n.getFirstChild()); int defaults = 0; for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) { validateSwitchMember(n.getLastChild()); if (c.isDefaultCase()) { defaults++; } } if (defaults > 1) { violation("Expected at most 1 'default' in switch but was " + defaults, n); } } private void validateSwitchMember(Node n) { switch (n.getType()) { case Token.CASE: validateCase(n); return; case Token.DEFAULT_CASE: validateDefault(n); return; default: violation("Expected switch member but was " + Token.name(n.getType()), n); } } private void validateDefault(Node n) { validateNodeType(Token.DEFAULT_CASE, n); validateChildCount(n, 1); validateSyntheticBlock(n.getLastChild()); } private void validateCase(Node n) { validateNodeType(Token.CASE, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validate

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>SyntheticBlock(n.getLastChild()); } private void validateOptionalExpression(Node n) { if (n.isEmpty()) { validateChildless(n); } else { validateExpression(n); } } private void validateChildless(Node n) { validateChildCount(n, 0); } private void validateAssignmentExpression(Node n) { validateChildCount(n, 2); validateAssignmentTarget(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateAssignmentTarget(Node n) { switch (n.getType()) { case Token.NAME: case Token.GETELEM: case Token.GETPROP: validateExpression(n); return; default: violation("Expected assignment target expression but was " + Token.name(n.getType()), n); } } private void validateGetProp(Node n) { validateNodeType(Token.GETPROP, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); Node prop = n.getLastChild(); validateNodeType(Token.STRING, prop); validateNonEmptyString(prop); } private void validateRegExpLit(Node n) { validateNodeType(Token.REGEXP, n); validateMinimumChildCount(n, 1); validateMaximumChildCount(n, 2); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateString(c); } } private void validateString(Node n) { validateNodeType(Token.STRING, n); validateChildCount(n, 0); try { // Validate that getString doesn't throw n.getString(); } catch (UnsupportedOperationException e) { violation("Invalid STRING node.", n); } } private void validateNumber(Node n) { validateNodeType(Token.NUMBER, n); validateChildCount(n, 0); try { // Validate that getDouble doesn't throw n.getDouble(); } catch (UnsupportedOperationException e) { violation("Invalid NUMBER node.", n); } } private void validateArrayLit(Node n) { validateNodeType(Token.ARRAYLIT, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // EMPTY is allowed to represent empty slots. validateOptionalExpression(c); } } private void validateObjectLit(Node n) { validate

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>NodeType(Token.OBJECTLIT, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateObjectLitKey(c); } } private void validateObjectLitKey(Node n) { switch (n.getType()) { case Token.GETTER_DEF: validateObjectLitGetKey(n); return; case Token.SETTER_DEF: validateObjectLitSetKey(n); return; case Token.STRING_KEY: validateObjectLitStringKey(n); return; default: violation("Expected object literal key expression but was " + Token.name(n.getType()), n); } } private void validateObjectLitGetKey(Node n) { validateNodeType(Token.GETTER_DEF, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit get functions must be nameless, and must have zero parameters. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (functionParams.hasChildren()) { violation("get methods must not have parameters.", n); } } private void validateObjectLitSetKey(Node n) { validateNodeType(Token.SETTER_DEF, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit set functions must be nameless, and must have 1 parameter. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (!functionParams.hasOneChild()) { violation("set methods must have exactly one parameter.", n); } } private void validateObjectLitStringKey(Node n) { validateNodeType(Token.STRING_KEY, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); validateExpression(n.getFirstChild()); } private void validateObjectLiteralKeyName(Node n) { if (n.isQuotedString()) { try { // Validate that getString doesn't throw n.getString(); } catch (UnsupportedOperationException e) { violation("getString failed for" + Token.name(n.getType

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>()), n); } } else { validateNonEmptyString(n); } } private void validateUnaryOp(Node n) { validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateBinaryOp(Node n) { validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateTrinaryOp(Node n) { validateChildCount(n, 3); Node first = n.getFirstChild(); validateExpression(first); validateExpression(first.getNext()); validateExpression(n.getLastChild()); } private void violation(String message, Node n) { violationHandler.handleViolation(message, n); } private void validateNodeType(int type, Node n) { if (n.getType() != type) { violation( "Expected " + Token.name(type) + " but was " + Token.name(n.getType()), n); } } private void validateChildCount(Node n, int i) { boolean valid = false; if (i == 0) { valid = !n.hasChildren(); } else if (i == 1) { valid = n.hasOneChild(); } else { valid = (n.getChildCount() == i); } if (!valid) { violation( "Expected " + i + " children, but was " + n.getChildCount(), n); } } private void validateMinimumChildCount(Node n, int i) { boolean valid = false; if (i == 1) { valid = n.hasChildren(); } else if (i == 2) { valid = n.hasMoreThanOneChild(); } else { valid = n.getChildCount() >= i; } if (!valid) { violation( "Expected at least " + i + " children, but was " + n.getChildCount(), n); } } private void validateMaximumChildCount(Node n, int i) { boolean valid = false; if (i == 1) { valid = !n.hasMoreThanOneChild(); } else { valid = n.getChildCount() <= i; } if (!valid) { violation( "Expected no more than " + i + " children, but was " + n.getChildCount(), n); } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> parent) { inputId = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); inputId = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top-level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.isCatch() && parent.isCatch()) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); if (!allowDupe) { compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(NodeUtil.getSourceName(n), n,

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning( "JSC_ARGUMENTS_ASSIGNMENT", "the \"arguments\" object cannot be reassigned in ES5 strict mode"); static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning( "JSC_DELETE_VARIABLE", "variables, functions, and arguments cannot be deleted in " + "ES5 strict mode"); static final DiagnosticType ILLEGAL_NAME = DiagnosticType.error( "JSC_ILLEGAL_NAME", "identifiers ending in '__' cannot be used in Caja"); static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning( "JSC_DUPLICATE_OBJECT_KEY", "object literals cannot contain duplicate keys in ES5 strict mode"); private final AbstractCompiler compiler; private final boolean noVarCheck; private final boolean noCajaChecks; StrictModeCheck(AbstractCompiler compiler) { this(compiler, false, false); } StrictModeCheck( AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) { this.compiler = compiler; this.noVarCheck = noVarCheck; this.noCajaChecks = noCajaChecks; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); NodeTraversal.traverse(compiler, root, new NonExternChecks()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { if (!isDeclaration(n)) { checkNameUse(t, n); } } else if (n.isAssign()) { checkAssignment(t, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteral(t, n); } else if (n.isLabel()) { checkLabel(t, n); } } /** * Determines if the given name is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> goog.getMsg() node we add a pair node:sourcename and later * when we visit its parent we remove this pair. All nodes that are left at * the end of traversing are orphaned nodes. It means have no corresponding * var or property node. */ private final Map<Node, String> googMsgNodes = Maps.newHashMap(); private final CheckLevel checkLevel; /** * Creates JS message visitor. * * @param compiler the compiler instance * @param needToCheckDuplications whether to check duplicated messages in * traversed * @param style style that should be used during parsing * @param idGenerator generator that used for creating unique ID for the * message */ JsMessageVisitor(AbstractCompiler compiler, boolean needToCheckDuplications, JsMessage.Style style, JsMessage.IdGenerator idGenerator) { this.compiler = compiler; this.needToCheckDuplications = needToCheckDuplications; this.style = style; this.idGenerator = idGenerator; checkLevel = (style == JsMessage.Style.CLOSURE) ? CheckLevel.ERROR : CheckLevel.WARNING; // TODO(anatol): add flag that decides whether to process UNNAMED messages. // Some projects would not want such functionality (unnamed) as they don't // use SOY templates. } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) { compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(), checkLevel, MESSAGE_NODE_IS_ORPHANED)); } } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { String messageKey; boolean isVar; Node msgNode, msgNodeParent; switch (node.getType()) { case Token.NAME: // var MSG_HELLO = 'Message' if ((parent != null) && (parent.isVar())) { messageKey = node.getString(); isVar = true; } else { return; } msgNode = node.getFirstChild(); msgNodeParent = node; break; case Token.ASSIGN: // somenamespace.someclass.MSG_HELLO = 'Message' isVar = false; Node getProp = node.getFirstChild(); if

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> (!getProp.isGetProp()) { return; } Node propNode = getProp.getLastChild(); messageKey = propNode.getString(); msgNode = node.getLastChild(); msgNodeParent = node; break; case Token.CALL: // goog.getMsg() if (MSG_FUNCTION_NAME.equals(node.getFirstChild().getQualifiedName())) { googMsgNodes.put(node, traversal.getSourceName()); } return; default: return; } // Is this a message name? boolean isNewStyleMessage = msgNode != null && msgNode.isCall(); if (!isMessageName(messageKey, isNewStyleMessage)) { return; } if (msgNode == null) { compiler.report( traversal.makeError(node, MESSAGE_HAS_NO_VALUE, messageKey)); return; } // Just report a warning if a qualified messageKey that looks like a message // (e.g. "a.b.MSG_X") doesn't use goog.getMsg(). if (isNewStyleMessage) { googMsgNodes.remove(msgNode); } else if (style != JsMessage.Style.LEGACY) { compiler.report(traversal.makeError(node, checkLevel, MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX)); } boolean isUnnamedMsg = isUnnamedMessageName(messageKey); Builder builder = new Builder( isUnnamedMsg ? null : messageKey); builder.setSourceName(traversal.getSourceName()); try { if (isVar) { extractMessageFromVariable(builder, node, parent, parent.getParent()); } else { extractMessageFromProperty(builder, node.getFirstChild(), node); } } catch (MalformedException ex) { compiler.report(traversal.makeError(ex.getNode(), MESSAGE_TREE_MALFORMED, ex.getMessage())); return; } JsMessage extractedMessage = builder.build(idGenerator); // If asked to check named internal messages. if (needToCheckDuplications && !isUnnamedMsg && !extractedMessage.isExternal()) { checkIfMessageDuplicated(traversal.getSourceName(), messageKey, msgNode); } if (extractedMessage.isEmpty()) { // value of the message is an empty string. Translators do not like it. compiler.report(traversal.makeError(node, MESSAGE_HAS_NO_TEXT,

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>formedException { // Determine the message's value Node valueNode = nameNode.getFirstChild(); switch (valueNode.getType()) { case Token.STRING: case Token.ADD: maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode, grandParentNode); builder.appendStringPart(extractStringFromStringExprNode(valueNode)); break; case Token.FUNCTION: maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode, grandParentNode); extractFromFunctionNode(builder, valueNode); break; case Token.CALL: maybeInitMetaDataFromJsDoc(builder, parentNode); extractFromCallNode(builder, valueNode); break; default: throw new MalformedException("Cannot parse value of message " + builder.getKey(), valueNode); } } /** * Creates a {@link JsMessage} for a JS message defined using an assignment to * a qualified name (e.g <code>a.b.MSG_X = goog.getMsg(...);</code>). * * @param builder the message builder * @param getPropNode a GETPROP node in a JS message assignment * @param assignNode an ASSIGN node, parent of {@code getPropNode}. * @throws MalformedException if {@code getPropNode} does not * correspond to a valid JS message node */ private void extractMessageFromProperty( Builder builder, Node getPropNode, Node assignNode) throws MalformedException { Node callNode = getPropNode.getNext(); maybeInitMetaDataFromJsDoc(builder, assignNode); extractFromCallNode(builder, callNode); } /** * Initializes the meta data in a JsMessage by examining the nodes just before * and after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param varNode the message VAR node * @param parentOfVarNode {@code varNode}'s parent node */ private void maybeInitMetaDataFromJsDocOrHelpVar( Builder builder, Node varNode, @Nullable Node parentOfVarNode) throws MalformedException { // First check description in @desc if (maybeInitMetaDataFromJsDoc(builder, varNode)) { return; } // Check the preceding node for meta data if ((parentOfVarNode != null) && maybeInitMetaDataFromHelpVar(builder, parentOfVarNode.getChildBefore(varNode))) { return;

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> String representation of the node * @throws MalformedException if the parsed message is invalid */ private static String extractStringFromStringExprNode(Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: return node.getString(); case Token.ADD: StringBuilder sb = new StringBuilder(); for (Node child : node.children()) { sb.append(extractStringFromStringExprNode(child)); } return sb.toString(); default: throw new MalformedException("STRING or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a FUNCTION node. * <p> * <pre> * The tree should look something like: * * function * |-- name * |-- lp * | |-- name <arg1> * | -- name <arg2> * -- block * | * --return * | * --add * |-- string foo * -- name <arg1> * </pre> * * @param builder the message builder * @param node the function node that contains a message * @throws MalformedException if the parsed message is invalid */ private void extractFromFunctionNode(Builder builder, Node node) throws MalformedException { Set<String> phNames = Sets.newHashSet(); for (Node fnChild : node.children()) { switch (fnChild.getType()) { case Token.NAME: // This is okay. The function has a name, but it is empty. break; case Token.PARAM_LIST: // Parse the placeholder names from the function argument list. for (Node argumentNode : fnChild.children()) { if (argumentNode.isName()) { String phName = argumentNode.getString(); if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, argumentNode); } else { phNames.add(phName); } } } break; case Token.BLOCK: // Build the message's value by examining the return statement Node returnNode = fnChild.getFirstChild(); if (!returnNode.isReturn()) { throw new MalformedException("RETURN node expected; found: " + getReadableTokenName(returnNode), returnNode); } for (Node child : returnNode.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>children()) { extractFromReturnDescendant(builder, child); } // Check that all placeholders from the message text have appropriate // object literal keys for (String phName : builder.getPlaceholders()) { if(!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, returnNode); } } break; default: throw new MalformedException( "NAME, LP, or BLOCK node expected; found: " + getReadableTokenName(node), fnChild); } } } /** * Appends value parts to the message builder by traversing the descendants * of the given RETURN node. * * @param builder the message builder * @param node the node from where we extract a message * @throws MalformedException if the parsed message is invalid */ private void extractFromReturnDescendant(Builder builder, Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: builder.appendStringPart(node.getString()); break; case Token.NAME: builder.appendPlaceholderReference(node.getString()); break; case Token.ADD: for (Node child : node.children()) { extractFromReturnDescendant(builder, child); } break; default: throw new MalformedException( "STRING, NAME, or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a CALL node. * <p> * The tree should look something like: * * <pre> * call * |-- getprop * | |-- name 'goog' * | +-- string 'getMsg' * | * |-- string 'Hi {$userName}! Welcome to {$product}.' * +-- objlit * |-- string 'userName' * |-- name 'someUserName' * |-- string 'product' * +-- call * +-- name 'getProductName' * </pre> * * @param builder the message builder * @param node the call node from where we extract the message * @throws MalformedException if the parsed message is invalid */ private void extractFromCallNode(Builder builder, Node node) throws MalformedException { // Check the function being called if (!node.isCall()) { throw new

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>.CLOSURE || isNewStyleMessage || !identifier.endsWith(DESC_SUFFIX)); } /** * Returns whether the given message name is in the unnamed namespace. */ private static boolean isUnnamedMessageName(String identifier) { return MSG_UNNAMED_PATTERN.matcher(identifier).matches(); } /** * Returns whether a string is nonempty, begins with a lowercase letter, and * contains only digits and underscores after the first underscore. */ static boolean isLowerCamelCaseWithNumericSuffixes(String input) { return CAMELCASE_PATTERN.matcher(input).matches(); } /** * Returns human-readable name of the given node's type. */ private static String getReadableTokenName(Node node) { return Token.name(node.getType()); } /** * Converts the given string from upper-underscore case to lower-camel case, * preserving numeric suffixes. For example: "NAME" -> "name" "A4_LETTER" -> * "a4Letter" "START_SPAN_1_23" -> "startSpan_1_23". */ static String toLowerCamelCaseWithNumericSuffixes(String input) { // Determine where the numeric suffixes begin int suffixStart = input.length(); while (suffixStart > 0) { char ch = '\0'; int numberStart = suffixStart; while (numberStart > 0) { ch = input.charAt(numberStart - 1); if (Character.isDigit(ch)) { numberStart--; } else { break; } } if ((numberStart > 0) && (numberStart < suffixStart) && (ch == '_')) { suffixStart = numberStart - 1; } else { break; } } if (suffixStart == input.length()) { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input); } else { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input.substring(0, suffixStart)) + input.substring(suffixStart); } } static class MalformedException extends Exception { private static final long serialVersionUID = 1L; private final Node node; MalformedException(String message, Node node) { super(message); this.node = node; } Node getNode() { return node; }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Check for usage of 'with'. * */ class ControlStructureCheck implements HotSwapCompilerPass { private final AbstractCompiler compiler; static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( "JSC_USE_OF_WITH", "The use of the 'with' structure should be avoided."); ControlStructureCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { check(root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { check(scriptRoot); } /** * Reports errors for any invalid use of control structures. * * @param node Current node to check. */ private void check(Node node) { switch (node.getType()) { case Token.WITH: JSDocInfo info = node.getJSDocInfo(); boolean allowWith = info != null && info.getSuppressions().contains("with"); if (!allowWith) { report(node, USE_OF_WITH); } break; } for (Node bChild = node.getFirstChild(); bChild != null;) { Node next = bChild.getNext(); check(bChild); bChild = next; } } private void report(Node n, DiagnosticType error) { compiler.report(JSError.make(n.getSourceFileName(), n, error)); } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * A builder for the Rhino Node representing Function parameters. * @author nicksantos@google.com (Nick Santos) */ public class FunctionParamBuilder { private final JSTypeRegistry registry; private final Node root = new Node(Token.PARAM_LIST); public FunctionParamBuilder(JSTypeRegistry registry) { this.registry = registry; } /** * Add parameters of the given type to the end of the param list. * @return False

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>.newString(Token.NAME, ""); paramNode.setJSType(type); root.addChildToBack(paramNode); return paramNode; } public Node build() { return root; } private boolean hasOptionalOrVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && (lastChild.isOptionalArg() || lastChild.isVarArgs()); } public boolean hasVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && lastChild.isVarArgs(); } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>nowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ protected JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(node, qualifiedName, origType, type); break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Maps; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Filters warnings based on in-code {@code @suppress} annotations. * @author nicksantos@google.com (Nick Santos) */ class SuppressDocWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; /** Warnings guards for each suppressible warnings group, indexed by name. */ private final Map<String, DiagnosticGroupWarningsGuard> suppressors = Maps.newHashMap(); /** * The suppressible groups, indexed by name. */ SuppressDocWarningsGuard(Map<String, DiagnosticGroup> suppressibleGroups) { for (Map.Entry<String, DiagnosticGroup> entry : suppressibleGroups.entrySet()) { suppressors.put( entry.getKey(), new DiagnosticGroupWarningsGuard( entry.getValue(), CheckLevel.OFF)); } } @Override public CheckLevel level(JSError error) { Node node = error.node; if (node != null) { for (Node current = node; current != null; current = current.getParent()) { int type = current.getType(); JSDocInfo info = null; // We only care about function annotations at the FUNCTION and SCRIPT // level. Otherwise, the @suppress annotation has an implicit // dependency on the exact structure of our AST, and that seems like // a bad idea. if (type == Token.FUNCTION) { info = NodeUtil.getFunctionJSDocInfo(current); } else

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> if (type == Token.SCRIPT) { info = current.getJSDocInfo(); } else if (type == Token.ASSIGN) { Node rhs = current.getLastChild(); if (rhs.isFunction()) { info = NodeUtil.getFunctionJSDocInfo(rhs); } } if (info != null) { for (String suppressor : info.getSuppressions()) { WarningsGuard guard = suppressors.get(suppressor); // Some @suppress tags are for other tools, and // may not have a warnings guard. if (guard != null) { CheckLevel newLevel = guard.level(error); if (newLevel != null) { return newLevel; } } } } } } return null; } @Override public int getPriority() { // Happens after path-based filtering, but before other times // of filtering. return WarningsGuard.Priority.SUPPRESS_DOC.value; } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> clauseNode); abstract T processConditionalExpression(ConditionalExpression exprNode); abstract T processContinueStatement(ContinueStatement statementNode); abstract T processDoLoop(DoLoop loopNode); abstract T processElementGet(ElementGet getNode); abstract T processEmptyExpression(EmptyExpression exprNode); abstract T processEmptyStatement(EmptyStatement exprNode); abstract T processExpressionStatement(ExpressionStatement statementNode); abstract T processForInLoop(ForInLoop loopNode); abstract T processForLoop(ForLoop loopNode); abstract T processFunctionCall(FunctionCall callNode); abstract T processFunctionNode(FunctionNode functionNode); abstract T processIfStatement(IfStatement statementNode); abstract T processInfixExpression(InfixExpression exprNode); abstract T processKeywordLiteral(KeywordLiteral literalNode); abstract T processLabel(Label labelNode); abstract T processLabeledStatement(LabeledStatement statementNode); abstract T processName(Name nameNode); abstract T processNewExpression(NewExpression exprNode); abstract T processNumberLiteral(NumberLiteral literalNode); abstract T processObjectLiteral(ObjectLiteral literalNode); abstract T processObjectProperty(ObjectProperty propertyNode); abstract T processParenthesizedExpression(ParenthesizedExpression exprNode); abstract T processPropertyGet(PropertyGet getNode); abstract T processRegExpLiteral(RegExpLiteral literalNode); abstract T processReturnStatement(ReturnStatement statementNode); abstract T processScope(Scope scopeNode); abstract T processStringLiteral(StringLiteral literalNode); abstract T processSwitchCase(SwitchCase caseNode); abstract T processSwitchStatement(SwitchStatement statementNode); abstract T processThrowStatement(ThrowStatement statementNode); abstract T processTryStatement(TryStatement statementNode); abstract T processUnaryExpression(UnaryExpression exprNode); abstract T processVariableDeclaration(VariableDeclaration declarationNode); abstract T processVariableInitializer(VariableInitializer initializerNode); abstract T processWhileLoop(WhileLoop loopNode); abstract T processWithStatement(WithStatement statementNode); abstract T processIllegalToken(AstNode node); public T process(AstNode node) { switch (node.getType()) { case Token.ADD: case Token.AND: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT:

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case Token.MOD: case Token.MUL: case Token.NE: case Token.OR: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return processInfixExpression((InfixExpression) node); case Token.ARRAYLIT: return processArrayLiteral((ArrayLiteral) node); case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_DIV: case Token.ASSIGN_LSH: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_RSH: case Token.ASSIGN_SUB: case Token.ASSIGN_URSH: return processAssignment((Assignment) node); case Token.BITNOT: case Token.DEC: case Token.DELPROP: case Token.INC: case Token.NEG: case Token.NOT: case Token.POS: case Token.TYPEOF: case Token.VOID: return processUnaryExpression((UnaryExpression) node); case Token.BLOCK: if (node instanceof Block) { return processBlock((Block) node); } else if (node instanceof Scope) { return processScope((Scope) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.BREAK: return processBreakStatement((BreakStatement) node); case Token.CALL: return processFunctionCall((FunctionCall) node); case Token.CASE: case Token.DEFAULT: return processSwitchCase((SwitchCase) node); case Token.CATCH: return processCatchClause((CatchClause) node); case Token.COLON: return processObjectProperty((ObjectProperty) node); case Token.CONTINUE: return processContinueStatement((ContinueStatement) node); case Token.DO: return processDoLoop((DoLoop) node); case Token.EMPTY: return (node instanceof EmptyExpression) ? processEmptyExpression((EmptyExpression) node) : processEmptyStatement((EmptyStatement) node); case Token.EXPR_RESULT: case Token.EXPR_VOID: if (node instanceof ExpressionStatement) { return processExpressionStatement((ExpressionStatement) node); } else if

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> (node instanceof LabeledStatement) { return processLabeledStatement((LabeledStatement) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.DEBUGGER: case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: return processKeywordLiteral((KeywordLiteral) node); case Token.FOR: if (node instanceof ForInLoop) { return processForInLoop((ForInLoop) node); } else if (node instanceof ForLoop) { return processForLoop((ForLoop) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.FUNCTION: return processFunctionNode((FunctionNode) node); case Token.GETELEM: return processElementGet((ElementGet) node); case Token.GETPROP: return processPropertyGet((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name) node); case Token.NEW: return processNewExpression((NewExpression) node); case Token.NUMBER: return processNumberLiteral((NumberLiteral) node); case Token.OBJECTLIT: return processObjectLiteral((ObjectLiteral) node); case Token.REGEXP: return processRegExpLiteral((RegExpLiteral) node); case Token.RETURN: return processReturnStatement((ReturnStatement) node); case Token.SCRIPT: return processAstRoot((AstRoot) node); case Token.STRING: return processStringLiteral((StringLiteral) node); case Token.SWITCH: return processSwitchStatement((SwitchStatement) node); case Token.THROW: return processThrowStatement((ThrowStatement) node); case Token.TRY: return processTryStatement((TryStatement) node); case Token.CONST: case Token.VAR: if (node instanceof VariableDeclaration) { return processVariableDeclaration((VariableDeclaration) node); } else if (node instanceof VariableInitializer) { return processVariable

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Initializer((VariableInitializer) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.WHILE: return processWhileLoop((WhileLoop) node); case Token.WITH: return processWithStatement((WithStatement) node); } return processIllegalToken(node); } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Set<String> CONSTRUCTORS_WITHOUT_SIDE_EFFECTS = new HashSet<String>(Arrays.asList( "Array", "Date", "Error", "Object", "RegExp", "XMLHttpRequest")); // Utility class; do not instantiate. private NodeUtil() {} /** * Gets the boolean value of a node that represents a expression. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function. * Note: unlike getBooleanValue this function does not return UNKNOWN * for expressions with side-effects. */ static TernaryValue getImpureBooleanValue(Node n) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: // For ASSIGN and COMMA the value is the value of the RHS. return getImpureBooleanValue(n.getLastChild()); case Token.NOT: TernaryValue value = getImpureBooleanValue(n.getLastChild()); return value.not(); case Token.AND: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.and(rhs); } case Token.OR: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.or(rhs); } case Token.HOOK: { TernaryValue trueValue = getImpureBooleanValue( n.getFirstChild().getNext()); TernaryValue falseValue = getImpureBooleanValue(n.getLastChild()); if (trueValue.equals(falseValue)) { return trueValue; } else { return TernaryValue.UNKNOWN; } } case Token.ARRAYLIT: case Token.OBJECTLIT: // ignoring side-effects return TernaryValue.TRUE; case Token.VOID: return TernaryValue.FALSE; default: return getPureBooleanValue(n); } } /** * Gets the boolean value of a node that represents a literal. This method * effectively emulates the <code>Boolean()</code> JavaScript cast function * except it return UNKNOWN for known values with side-effects, use * getExpressionBooleanValue if you don't care about side-effects. */ static TernaryValue getPureBooleanValue(Node n) { switch (n.getType()) { case Token

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NOT: return getPureBooleanValue(n.getLastChild()).not(); case Token.NULL: case Token.FALSE: return TernaryValue.FALSE; case Token.VOID: if (!mayHaveSideEffects(n.getFirstChild())) { return TernaryValue.FALSE; } break; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.REGEXP: return TernaryValue.TRUE; case Token.ARRAYLIT: case Token.OBJECTLIT: if (!mayHaveSideEffects(n)) { return TernaryValue.TRUE; } break; } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): regex literals as well. switch (n.getType()) { case Token.STRING: case Token.STRING_KEY: return n.getString(); case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name)) { return name; } break; case Token.NUMBER: return getStringValue(n.getDouble()); case Token.FALSE: return "false"; case Token.TRUE: return "true"; case Token.NULL: return "null"; case Token.VOID: return "undefined"; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? "false" : "true"; // reversed. } break;

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case Token.ARRAYLIT: return arrayToString(n); case Token.OBJECTLIT: return "[object Object]"; } return null; } static String getStringValue(double value) { long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(value); } } /** * When converting arrays to string using Array.prototype.toString or * Array.prototype.join, the rules for conversion to String are different * than converting each element individually. Specifically, "null" and * "undefined" are converted to an empty string. * @param n A node that is a member of an Array. * @return The string representation. */ static String getArrayElementStringValue(Node n) { return (NodeUtil.isNullOrUndefined(n) || n.isEmpty()) ? "" : getStringValue(n); } static String arrayToString(Node literal) { Node first = literal.getFirstChild(); StringBuilder result = new StringBuilder(); int nextSlot = 0; int nextSkipSlot = 0; for (Node n = first; n != null; n = n.getNext()) { String childValue = getArrayElementStringValue(n); if (childValue == null) { return null; } if (n != first) { result.append(','); } result.append(childValue); nextSlot++; } return result.toString(); } /** * Gets the value of a node as a Number, or null if it cannot be converted. * When it returns a non-null Double, this method effectively emulates the * <code>Number()</code> JavaScript cast function. */ static Double getNumberValue(Node n) { switch (n.getType()) { case Token.TRUE: return 1.0; case Token.FALSE: case Token.NULL: return 0.0; case Token.NUMBER: return n.getDouble(); case Token.VOID: if (mayHaveSideEffects(n.getFirstChild())) { return null; } else { return Double.NaN; } case Token.NAME: // Check for known constants String name = n.getString(); if (name.equals("undefined")) { return Double.NaN; } if (name.equals("NaN")) {

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> return Double.NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().isName() && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { // Attempt to convert hex numbers. try { return Double.valueOf(Integer.parseInt(s.substring(2), 16)); } catch (NumberFormatException e) { return Double.NaN; } } if (s.length() > 3 && (s.charAt(0) == '-' || s.charAt(0) == '+') && s.charAt(1) == '0' && (s.charAt(2) == 'x' || s.charAt(2) == 'X')) { // hex numbers with explicit signs vary between browsers. return null; } // Firefox and IE treat the "Infinity" differently. Firefox is case // insensitive, but IE treats "infinity" as NaN. So leave it alone. if (s.equals("infinity") || s.equals("-infinity") || s.equals("+infinity")) { return null; } try { return Double.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Gets the function's name. This method recognizes the forms: * <ul> * <li>{@code &#123;'name': function() ...&#125;}</li> * <li>{@code &#123;name: function() ...&#125;}</li> * <li>{@code function name() ...}</li> * <li>{@code var name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ public static String getNearestFunctionName(Node n) { String name = getFunctionName(n); if (name != null) { return name; } // Check for the form { 'x' : function() { } } Node parent = n.getParent(); switch (parent.getType()) { case Token.SETTER_DEF: case Token.GETTER_DEF: case Token.STRING_KEY: // Return the name of the literal's key. return parent.getString(); case Token.NUMBER: return getStringValue(parent); } return null; } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: return true; case Token.NOT: return isImmutableValue(n.getFirstChild()); case Token.VOID: case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if the operator on this node is symmetric */ public static boolean isSymmetricOperation(Node n) { switch (n.getType()) { case Token.EQ: // equal case Token.NE: // not equal case Token.SHEQ: // exactly equal case Token.SHNE: // exactly not equal case Token.MUL: // multiply, unlike add it only works on numbers // or results NaN if any of the operators is not a number return true; } return false; } /** * Returns true if the operator on this node is relational. * the returned set does not include the equalities. */ public static boolean isRelationalOperation(Node n) { switch (n.getType()) { case Token.GT: // equal case Token.GE: // not equal case Token.LT: // exactly equal case Token.LE: // exactly not equal return true; } return false; } /** * Returns the inverse of an operator if it is invertible. * ex. '>' ==> '<' */ public static int getInverseOperator(int type) { switch (type) { case Token.GT: return Token.LT; case Token.LT: return Token.GT; case Token.GE: return Token.LE; case Token.LE: return Token.GE; } return Token.ERROR; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * the name a is not. * * Function literals do not meet this definition, because they * lexically capture variables. For example, if you have * <code> * function() { return a; } * </code> * If it is evaluated in a different scope, then it * captures a different variable. Even if the function did not read * any captured variables directly, it would still fail this definition, * because it affects the lifecycle of variables in the enclosing scope. * * However, a function literal with respect to a particular

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> scope is * a literal. * * @param includeFunctions If true, all function expressions will be * treated as literals. */ static boolean isLiteralValue(Node n, boolean includeFunctions) { switch (n.getType()) { case Token.ARRAYLIT: for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Unary

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (!block.isBlock()) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (!n.isEmpty()) { return false; } } return true; } static boolean isSimpleOperator(Node n) { return isSimpleOperatorType(n.getType()); } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GETELEM: case Token.GETPROP: case Token.GT: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.NOT: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.TYPEOF: case Token.VOID: case Token.POS: case Token.NEG: case Token.URSH: return true; default: return false; } } /** * Creates an EXPR_RESULT. * * @param child The expression itself. * @return Newly created EXPR node with the child as subexpression. */ public static Node newExpr(Node child) { return IR.exprResult(child).srcref(child); } /** * Returns

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> true if the node may create new mutable state, or change existing * state. * * @see <a href="http://www.xkcd.org/326/">XKCD Cartoon</a> */ static boolean mayEffectMutableState(Node n) { return mayEffectMutableState(n, null); } static boolean mayEffectMutableState(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, true, compiler); } /** * Returns true if the node which may have side effects when executed. */ static boolean mayHaveSideEffects(Node n) { return mayHaveSideEffects(n, null); } static boolean mayHaveSideEffects(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, false, compiler); } /** * Returns true if some node in n's subtree changes application state. * If {@code checkForNewObjects} is true, we assume that newly created * mutable objects (like object literals) change state. Otherwise, we assume * that they have no side effects. */ private static boolean checkForStateChangeHelper( Node n, boolean checkForNewObjects, AbstractCompiler compiler) { // Rather than id which ops may have side effects, id the ones // that we know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.PARAM_LIST: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.STRING_KEY: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: if (checkForNewObjects) { return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper( c.getFirstChild(), checkForNewObjects, compiler)) { return true; } } return false; case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) { return true; } break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return checkForNewObjects || !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperatorType(n.getType())) { break; } if (isAssignmentOp(n)) { Node assignTarget = n.getFirstChild(); if (assignTarget.isName()) { return true; } // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } if (isGet(assignTarget)) { // If the object being assigned to is a local object, don't // consider this a side-effect as it can't be referenced // elsewhere. Don't do this recursively as the property might // be an alias of another object, unlike a literal below. Node current = assignTarget.getFirstChild(); if (evaluatesToLocalValue(current)) { return false; } // A literal value as defined by "isLiteralValue" is guaranteed // not to be an alias, or any components which are aliases of // other objects. // If the root object is a literal don't consider this a // side-effect. while (isGet(current)) { current = current.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>getFirstChild(); } return !isLiteralValue(current, true); } else { // TODO(johnlenz): remove this code and make this an exception. This // is here only for legacy reasons, the AST is not valid but // preserve existing behavior. return !isLiteralValue(assignTarget, true); } } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - constructor call node */ static boolean constructorCallHasSideEffects(Node callNode) { return constructorCallHasSideEffects(callNode, null); } static boolean constructorCallHasSideEffects( Node callNode, AbstractCompiler compiler) { if (!callNode.isNew()) { throw new IllegalStateException( "Expected NEW node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.isName() && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } // A list of built-in object creation or primitive type cast functions that // can also be called as constructors but lack side-effects. // TODO(johnlenz): consider adding an extern annotation for this. private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS = ImmutableSet.of( "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); private static final Set<String> OBJECT_METHODS_WITHOUT_SIDEEFFECTS = ImmutableSet.of("toString", "valueOf"); private static final Set<String> REGEXP_METHODS = ImmutableSet.of("test", "exec"); private static final Set<String> STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects(Node callNode) { return functionCallHasSideEffects

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The call node to inspected. * @param compiler A compiler object to provide program state changing * context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { if (!callNode.isCall()) { throw new IllegalStateException( "Expected CALL node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.isName()) { String name = nameNode.getString(); if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) { return false; } } else if (nameNode.isGetProp()) { if (callNode.hasOneChild() && OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains( nameNode.getLastChild().getString())) { return false; } if (callNode.isOnlyModifiesThisCall() && evaluatesToLocalValue(nameNode.getFirstChild())) { return false; } // Math.floor has no side-effects. // TODO(nicksantos): This is a terrible terrible hack, until // I create a definitionProvider that understands namespacing. if (nameNode.getFirstChild().isName()) { if ("Math.floor".equals(nameNode.getQualifiedName())) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().isRegExp() && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false; } else if (nameNode.getFirstChild().isString() && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.isString() || param.isRegExp())) return false; } } } return true; } /** * @return Whether the call has a local result. */ static boolean callHasLocalResult(Node n) { Preconditions.checkState(n.isCall()); return (n.getSideEffectFlags()

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> & Node.FLAG_LOCAL_RESULTS) > 0; } /** * @return Whether the new has a local result. */ static boolean newHasLocalResult(Node n) { Preconditions.checkState(n.isNew()); return n.isOnlyModifiesThisCall(); } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return 6; case Token.BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9;

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.CALL: case Token.GETELEM: case Token.GETPROP: // Data values case Token.ARRAYLIT: case Token.EMPTY: // TODO(johnlenz): remove this. case Token.FALSE: case Token.FUNCTION: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.STRING: case Token.STRING_KEY: case Token.THIS: case Token.TRUE: return 15; default: throw new Error("Unknown precedence for " + Token.name(type) + " (type " + type + ")"); } } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean valueCheck(Node n, Predicate<Node> p) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: return valueCheck(n.getLastChild(), p); case Token.AND: case Token.OR: return valueCheck(n.getFirstChild(), p) && valueCheck(n.getLastChild(), p); case Token.HOOK: return valueCheck(n.getFirstChild().getNext(), p) && valueCheck(n.getLastChild(), p); default: return p.apply(n); } } static class NumbericResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return isNumericResultHelper(n); } } static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE = new NumbericResultPredicate(); /** * Returns true if the result of node evaluation is always a number */ static boolean isNumericResult(Node n) { return valueCheck(n, NUMBERIC_RESULT_PREDICATE);

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> } static boolean isNumericResultHelper(Node n) { switch (n.getType()) { case Token.ADD: return !mayBeString(n.getFirstChild()) && !mayBeString(n.getLastChild()); case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MUL: case Token.MOD: case Token.DIV: case Token.INC: case Token.DEC: case Token.POS: case Token.NEG: case Token.NUMBER: return true; case Token.NAME: String name = n.getString(); if (name.equals("NaN")) { return true; } if (name.equals("Infinity")) { return true; } return false; default: return false; } } static class BooleanResultPredicate implements Predicate<Node> { @Override public boolean apply(Node n) { return isBooleanResultHelper(n); } } static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE = new BooleanResultPredicate(); /** * @return Whether the result of node evaluation is always a boolean */ static boolean isBooleanResult(Node n) { return valueCheck(n, BOOLEAN_RESULT_PREDICATE); } static boolean isBooleanResultHelper(Node n) { switch (n.getType()) { // Primitives case Token.TRUE: case Token.FALSE: // Comparisons case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: // Queries case Token.IN: case Token.INSTANCEOF: // Inversion case Token.NOT: // delete operator returns a boolean. case Token.DELPROP: return true; default: return false; } } static boolean isUndefined(Node n) { switch (n.getType()) { case Token.VOID: return true; case Token.NAME: return n.getString().equals("undefined"); } return false; } static boolean isNullOrUndefined(Node n) { return n.isNull() || isUndefined(n); } static class MayBeStringResult

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Predicate implements Predicate<Node> { @Override public boolean apply(Node n) { return mayBeStringHelper(n); } } static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE = new MayBeStringResultPredicate(); /** * @returns Whether the results is possibly a string. */ static boolean mayBeString(Node n) { return mayBeString(n, true); } static boolean mayBeString(Node n, boolean recurse) { if (recurse) { return valueCheck(n, MAY_BE_STRING_PREDICATE); } else { return mayBeStringHelper(n); } } static boolean mayBeStringHelper(Node n) { return !isNumericResult(n) && !isBooleanResult(n) && !isUndefined(n) && !n.isNull(); } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITXOR: case Token.BITAND: return true; default: return false; } } /** * Returns true if the operator is commutative. * e.g. (a * b) * c = c * (b * a) * Note 1: "+" is not commutative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 * Note 2: only operations on literals and pure functions are commutative. */ static boolean isCommutative(int type) { switch (type) { case Token.MUL: case Token.BITOR: case Token.BITXOR: case Token.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BITXOR: return Token.BITXOR; case Token.ASSIGN_BITAND: return Token.BITAND; case Token.ASSIGN_LSH: return Token.LSH; case Token.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assignment op:" + n); } /** * Determines if the given node contains a function statement or function * expression. */ static boolean containsFunction(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the shallow scope contains references to 'this' keyword */ static boolean referencesThis(Node n) { Node start = (n.isFunction()) ? n.getLastChild() : n; return containsType(start, Token.THIS, MATCH_NOT_FUNCTION); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.isGetProp() || n.isGetElem(); } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.isName() && n.getParent().isVar(); } /**

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); if (parent.isVar()) { return n.getFirstChild(); } else if (parent.isAssign() && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this node an assignment expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is ASSIGN */ static boolean isExprAssign(Node n) { return n.isExprResult() && n.getFirstChild().isAssign(); } /** * Is this node a call expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is CALL */ static boolean isExprCall(Node n) { return n.isExprResult() && n.getFirstChild().isCall(); } /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.isFor() && n.getChildCount() == 3; } /** * Determines whether the given node is a FOR, DO, or WHILE node. */ static boolean isLoopStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * @param n The node to inspect. * @return If the node, is a FOR, WHILE, or DO, it returns the node for * the code BLOCK, null otherwise. */ static Node getLoopCodeBlock(Node n) { switch (n.getType()) { case Token.FOR: case Token.WHILE: return n.getLastChild(); case Token.DO: return n.getFirstChild(); default: return null; } } /** * @return Whether the specified node has a loop parent that * is within the current scope. */ static boolean isWithinLoop(Node n) { for (Node parent : n.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>getAncestors()) { if (NodeUtil.isLoopStructure(parent)) { return true; } if (parent.isFunction()) { break; } } return false; } /** * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. */ static boolean isControlStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT_CASE: return true; default: return false; } } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT_CASE: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.isScript() || n.isBlock(); } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { return isStatementParent(n.getParent()); } static boolean isStatementParent(Node parent) { // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.isCase() || n.isDefaultCase(); } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return n.isName() && !n.getString().isEmpty(); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.isTry() && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Whether the node is a CATCH container BLOCK. */ static boolean isTryCatchNodeContainer(Node n) { Node parent = n.getParent(); return parent.isTry() && parent.getFirstChild().getNext() == n; } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { if (isTryFinallyNode(parent, node)) { if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) { // A finally can only be removed if there is a catch. parent.removeChild(node); } else { // Otherwise, only its children can be removed. node.detachChildren(); } } else if (node.isCatch()) { // The CATCH can can only

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Preconditions.checkState(block.isBlock()); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else { return false; } } /** * @param node A node * @return Whether the call is a NEW or CALL node. */ static boolean isCallOrNew(Node node) { return node.isCall() || node.isNew(); } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(fn.isFunction()); return fn.getLastChild(); } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.isFunction() && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().isScript() || n.getParent().getParent().isFunction()); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to the current scope. * * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> expressions: * <pre

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { switch (node.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GETTER_DEF: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = valueType.toMaybeFunctionType(); valueType = fntype.getReturnType(); } else { return null; } break; case Token.SETTER_DEF: if (valueType.isFunctionType()) { // SET must always return a function type. FunctionType fntype = valueType.toMaybeFunctionType(); Node param = fntype.getParametersNode().getFirstChild(); // SET function must always have one parameter. valueType = param.getJSType(); } else { return null; } break; } } return valueType; } /** * Determines whether a node represents an object literal get or set key * (e.g. key1 in {get key1() {}, set key2(a){}). * * @param node A node */ static boolean isGetOrSetKey(Node node) { switch (node.getType()) { case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Converts an operator's token value (see {@link

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.BITOR: return "|"; case Token.OR: return "||"; case Token.BITXOR: return "^"; case Token.AND: return "&&"; case Token.BITAND: return "&"; case Token.SHEQ: return "==="; case Token.EQ: return "=="; case Token.NOT: return "!"; case Token.NE: return "!="; case Token.SHNE: return "!=="; case Token.LSH: return "<<"; case Token.IN: return "in"; case Token.LE: return "<="; case Token.LT: return "<"; case Token.URSH: return ">>>"; case Token.RSH: return ">>"; case Token.GE: return ">="; case Token.GT: return ">"; case Token.MUL: return "*"; case Token.DIV: return "/"; case Token.MOD: return "%"; case Token.BITNOT: return "~"; case Token.ADD: return "+"; case Token.SUB: return "-"; case Token.POS: return "+"; case Token.NEG: return "-"; case Token.ASSIGN: return "="; case Token.ASSIGN_BITOR: return "|="; case Token.ASSIGN_BITXOR: return "^="; case Token.ASSIGN_BITAND: return "&="; case Token.ASSIGN_LSH: return "<<="; case Token.ASSIGN_RSH: return ">>="; case Token.ASSIGN_URSH: return ">>>="; case Token.ASSIGN_ADD: return "+="; case Token.ASSIGN_SUB: return "-="; case Token.ASSIGN_MUL: return "*="; case Token.ASSIGN_DIV: return "/="; case Token.ASSIGN_MOD: return "%="; case Token.VOID: return "void"; case Token.TYPEOF: return "typeof"; case Token.INSTANCEOF: return "instanceof"; default: return null; } } /** * Converts an operator's token value (see {@link Token}) to a string * representation or fails. * * @param operator the operator's token value to convert *

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> @return the string representation * @throws Error if the token value is not an operator */ static String opToStrNoFail(int operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { adding

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> based on call target. */ static Node newCallNode(Node callTarget, Node... parameters) { boolean isFreeCall = !isGet(callTarget); Node call = IR.call(callTarget); call.putBooleanProp(Node.FREE_CALL, isFreeCall); for (Node parameter : parameters) { call.addChildToBack(parameter); } return call; } /** * @return Whether the node is known to be a value that is not referenced * elsewhere. */ static boolean evaluatesToLocalValue(Node value) { return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse()); } /** * @param locals A predicate to apply to unknown local values. * @return Whether the node is known to be a value that is not a reference * outside the expression scope. */ static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) { switch (value.getType()) { case Token.ASSIGN: // A result that is aliased by a non-local name, is the effectively the // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluatesToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.INC: case Token.DEC: if (value.getBooleanProp(Node.INCRDECR_PROP)) { return evaluatesToLocalValue(value.getFirstChild(), locals); } else { return true; } case Token.THIS: return locals.apply(value); case Token.NAME: return isImmutableValue(value) || locals.apply(value); case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return locals.apply(value); case Token.CALL: return callHasLocalResult(value) || isToStringMethodCall(value) || locals.apply(value); case Token.NEW: return new

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>HasLocalResult(value) || locals.apply(value); case Token.FUNCTION: case Token.REGEXP: case Token.ARRAYLIT: case Token.OBJECTLIT: // Literals objects with non-literal children are allowed. return true; case Token.DELPROP: case Token.IN: // TODO(johnlenz): should IN operator be included in #isSimpleOperator? return true; default: // Other op force a local value: // x = '' + g (x is now an local string) // x -= g (x is now an local number) if (isAssignmentOp(value) || isSimpleOperator(value) || isImmutableValue(value)) { return true; } throw new IllegalStateException( "Unexpected expression node" + value + "\n parent:" + value.getParent()); } } /** * Given the first sibling, this returns the nth * sibling or null if no such sibling exists. * This is like "getChildAtIndex" but returns null for non-existent indexes. */ private static Node getNthSibling(Node first, int index) { Node sibling = first; while (index != 0 && sibling != null) { sibling = sibling.getNext(); index--; } return sibling; } /** * Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return propNode.isString() && "toString".equals(propNode.getString()); } return false; } /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.getJSD

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Value.getParent() == null) { return null; } if (isObjectLitKey(lValue, lValue.getParent())) { Node owner = getBestLValue(lValue.getParent()); if (owner != null) { String ownerName = getBestLValueName(owner); if (ownerName != null) { return ownerName + "." + getObjectLitKeyName(lValue); } } return null; } return lValue.getQualifiedName(); } /** * @returns false iff the result of the expression is not consumed. */ static boolean isExpressionResultUsed(Node expr) { // TODO(johnlenz): consider sharing some code with trySimpleUnusedResult. Node parent = expr.getParent(); switch (parent.getType()) { case Token.EXPR_RESULT: return false; case Token.HOOK: case Token.AND: case Token.OR: return (expr == parent.getFirstChild()) ? true : isExpressionResultUsed(parent); case Token.COMMA: return (expr == parent.getFirstChild()) ? false : isExpressionResultUsed(parent); case Token.FOR: if (!NodeUtil.isForIn(parent)) { // Only an expression whose result is in the condition part of the // expression is used. return (parent.getChildAtIndex(1) == expr); } break; } return true; } /** * @param n The expression to check. * @return Whether the expression is unconditionally executed only once in the * containing execution scope. */ static boolean isExecutedExactlyOnce(Node n) { inspect: do { Node parent = n.getParent(); switch (parent.getType()) { case Token.IF: case Token.HOOK: case Token.AND: case Token.OR: if (parent.getFirstChild() != n) { return false; } // other ancestors may be conditional continue inspect; case Token.FOR: if (NodeUtil.isForIn(parent)) { if (parent.getChildAtIndex(1) != n) { return false; } } else { if (parent.getFirstChild() != n) { return false; } } // other ancestors may be conditional continue inspect; case Token.WHILE: case Token.DO: return false; case Token.TRY: // Consider all code under a try/catch to be conditionally executed

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>. if (!hasFinally(parent) || parent.getLastChild() != n) { return false; } continue inspect; case Token.CASE: case Token.DEFAULT_CASE: return false; case Token.SCRIPT: case Token.FUNCTION: // Done, we've reached the scope root. break inspect; } } while ((n = n.getParent()) != null); return true; } static Node booleanNode(boolean value) { return value ? IR.trueNode() : IR.falseNode(); } static Node numberNode(double value, Node srcref) { Node result; if (Double.isNaN(value)) { result = IR.name("NaN"); } else if (value == Double.POSITIVE_INFINITY) { result = IR.name("Infinity"); } else if (value == Double.NEGATIVE_INFINITY) { result = IR.neg(IR.name("Infinity")); } else { result = IR.number(value); } if (srcref != null) { result.srcrefTree(srcref); } return result; } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>; this.nameNode = nameNode; this.type = type; this.scope = scope; this.index = index; this.input = input; this.isDefine = isDefine; this.info = info; this.typeInferred = inferred; } /** * Gets the name of the variable. */ @Override public String getName() { return name; } /** * Gets the node for the name of the variable. */ @Override public Node getNode() { return nameNode; } CompilerInput getInput() { return input; } @Override public StaticSourceFile getSourceFile() { return nameNode.getStaticSourceFile(); } @Override public Var getSymbol() { return this; } @Override public Var getDeclaration() { return nameNode == null ? null : this; } /** * Gets the parent of the name node. */ public Node getParentNode() { return nameNode == null ? null : nameNode.getParent(); } /** * Whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope. */ public boolean isBleedingFunction() { return NodeUtil.isFunctionExpression(getParentNode()); } /** * Gets the scope where this variable is declared. */ Scope getScope() { return scope; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return nameNode != null && NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotated by {@code @define}. */ public boolean isDefine() { return isDefine; } public Node getInitialValue() { Node parent = getParentNode(); int pType = parent.getType(); if (pType ==

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Token.FUNCTION) { return parent; } else if (pType == Token.ASSIGN) { return parent.getLastChild(); } else if (pType == Token.VAR) { return nameNode.getFirstChild(); } else { return null; } } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isTypeInferred()}. */ @Override public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ @Override public JSDocInfo getJSDocInfo() { return info; } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type = type.resolve(errorReporter, scope); } } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ @Override public boolean isTypeInferred() { return typeInferred; } public String getInputName() { if (input == null) return "<non-file>"; else return input.getName(); } public boolean isNoShadow() { if (info != null && info.isNoShadow()) { return true; } else { return false; } } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name + "{" + type + "}"; } /** Record that this is escaped by an inner scope. */ void markEscaped() { markedEscaped = true; } /** * Whether this is escaped by an inner scope. * Notice that not all scope creators record this information. */ boolean isMarked

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> final CharMatcher ID_MATCHER = CharMatcher.inRange('a', 'z'). or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.anyOf("0123456789_.")); // Warnings and Errors. static final DiagnosticType UNKNOWN_TWEAK_WARNING = DiagnosticType.warning( "JSC_UNKNOWN_TWEAK_WARNING", "no tweak registered with ID {0}"); static final DiagnosticType TWEAK_MULTIPLY_REGISTERED_ERROR = DiagnosticType.error( "JSC_TWEAK_MULTIPLY_REGISTERED_ERROR", "Tweak {0} has already been registered."); static final DiagnosticType NON_LITERAL_TWEAK_ID_ERROR = DiagnosticType.error( "JSC_NON_LITERAL_TWEAK_ID_ERROR", "tweak ID must be a string literal"); static final DiagnosticType INVALID_TWEAK_DEFAULT_VALUE_WARNING = DiagnosticType.warning( "JSC_INVALID_TWEAK_DEFAULT_VALUE_WARNING", "tweak {0} registered with {1} must have a default value that is a " + "literal of type {2}"); static final DiagnosticType NON_GLOBAL_TWEAK_INIT_ERROR = DiagnosticType.error( "JSC_NON_GLOBAL_TWEAK_INIT_ERROR", "tweak declaration {0} must occur in the global scope"); static final DiagnosticType TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR = DiagnosticType.error( "JSC_TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR", "Cannot override the default value of tweak {0} after it has been " + "registered"); static final DiagnosticType TWEAK_WRONG_GETTER_TYPE_WARNING = DiagnosticType.warning( "JSC_TWEAK_WRONG_GETTER_TYPE_WARNING", "tweak getter function {0} used for tweak registered using {1}"); static final DiagnosticType INVALID_TWEAK_ID_ERROR = DiagnosticType.error( "JSC_INVALID_TWEAK_ID_ERROR", "tweak ID contains illegal characters. Only letters, numbers, _ " + "and . are allowed"); /** * An enum of goog.tweak functions. */ private static enum TweakFunction { REGISTER_BOOLEAN("goog.tweak.registerBoolean", "boolean", Token.TRUE, Token.

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>FALSE), REGISTER_NUMBER("goog.tweak.registerNumber", "number", Token.NUMBER), REGISTER_STRING("goog.tweak.registerString", "string", Token.STRING), OVERRIDE_DEFAULT_VALUE("goog.tweak.overrideDefaultValue"), GET_COMPILER_OVERRIDES("goog.tweak.getCompilerOverrides_"), GET_BOOLEAN("goog.tweak.getBoolean", REGISTER_BOOLEAN), GET_NUMBER("goog.tweak.getNumber", REGISTER_NUMBER), GET_STRING("goog.tweak.getString", REGISTER_STRING); final String name; final String expectedTypeName; final int validNodeTypeA; final int validNodeTypeB; final TweakFunction registerFunction; TweakFunction(String name) { this(name, null, Token.ERROR, Token.ERROR, null); } TweakFunction(String name, String expectedTypeName, int validNodeTypeA) { this(name, expectedTypeName, validNodeTypeA, Token.ERROR, null); } TweakFunction(String name, String expectedTypeName, int validNodeTypeA, int validNodeTypeB) { this(name, expectedTypeName, validNodeTypeA, validNodeTypeB, null); } TweakFunction(String name, TweakFunction registerFunction) { this(name, null, Token.ERROR, Token.ERROR, registerFunction); } TweakFunction(String name, String expectedTypeName, int validNodeTypeA, int validNodeTypeB, TweakFunction registerFunction) { this.name = name; this.expectedTypeName = expectedTypeName; this.validNodeTypeA = validNodeTypeA; this.validNodeTypeB = validNodeTypeB; this.registerFunction = registerFunction; } boolean isValidNodeType(int type) { return type == validNodeTypeA || type == validNodeTypeB; } boolean isCorrectRegisterFunction(TweakFunction registerFunction) { Preconditions.checkNotNull(registerFunction); return this.registerFunction == registerFunction; } boolean isGetterFunction() { return registerFunction != null; } String getName() { return name; } String getExpectedTypeName() { return expectedTypeName; } Node createDefaultValueNode() { switch (this) { case REGISTER_BOOLEAN: return IR.falseNode(); case REGISTER_NUMBER: return IR.number(0); case REGISTER_STRING: return IR.string(""); } throw new IllegalStateException(); } } // A map of

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> throw new IllegalStateException("unexpect prop id " + propType); } } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { boolean equivalent = super.isEquivalentTo(node, compareJsType, recurse); if (equivalent) { double thisValue = getDouble(); double thatValue = ((NumberNode) node).getDouble(); if (thisValue == thatValue) { // detect the difference between 0.0 and -0.0. return (thisValue != 0.0) || (1/thisValue == 1/thatValue); } } return false; } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { return (super.isEquivalentTo(node, compareJsType, recurse) && this.str.equals(((StringNode) node).

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument(children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean hasChildren() { return first != null; } public Node getFirstChild() { return first; } public Node getLastChild() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) { return null; } Node n = first; while (n.next != child) { n = n.next; if (n == null) { throw new RuntimeException("node is not a child"); } } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public int getIndexOfChild(Node child)

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Prop(propType) != 0; } /** * Returns the integer value for the property, or 0 if the property * is not defined. */ public int getIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return 0; } return item.getIntValue(); } public int getExistingIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { throw new IllegalStateException("missing prop: " + propType); } return item.getIntValue(); } public void putProp(int propType, Object value) { if (propType == SOURCENAME_PROP) { putProp( STATIC_SOURCE_FILE, new SimpleSourceFile((String) value, false)); return; } removeProp(propType); if (value != null) { propListHead = createProp(propType, value, propListHead); } } public void putBooleanProp(int propType, boolean value) { putIntProp(propType, value ? 1 : 0); } public void putIntProp(int propType, int value) { removeProp(propType); if (value != 0) { propListHead = createProp(propType, value, propListHead); } } PropListItem createProp(int propType, Object value, PropListItem next) { return new ObjectPropListItem(propType, value, next); } PropListItem createProp(int propType, int value, PropListItem next) { return new IntPropListItem(propType, value, next); } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count--; keys[count] = x.getType(); } Arrays.sort(keys); return keys; } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first == null || first.getType() != Token.NAME) { sb.append("<invalid>"); } else { sb.append(first.getString()); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { return isEquivalentTo(node, false, true); } /** * Returns true if this node is equivalent semantically to another and * the types are equivalent. */ public boolean isEquivalentToTyped(Node node) { return isEquivalentTo(node, true, true); } /** * @param compareJsType Whether to compare the JSTypes of the nodes. * @param recurse Whether to compare the children of the current node, if * not only the the count of the children are compared. * @return Whether this node is equivalent semantically to the provided node. */ boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { if (type != node.getType() || getChildCount() != node.getChildCount() || this.getClass() != node.getClass()) { return false; } if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) { return false; } if (type == Token.INC || type == Token.DEC) { int post1 = this.getIntProp(INCRDECR_PROP); int post2 = node.getIntProp(INCRDECR_PROP); if (post1 != post2) { return false; } } else if (type == Token.STRING || type == Token.STRING_KEY) { if (type == Token.STRING_KEY) { int quoted1 = this.getIntProp(QUOTED_PROP); int quoted2 = node.getIntProp(QUOTED_PROP); if (quoted1 != quoted2) { return false; } } int slashV1 = this.getIntProp(SLASH_V); int slashV2 = node.getIntProp(SLASH_V); if (slashV1 != slashV2) { return false; } } else if (type == Token.CALL) { if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) {

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> return false; } } if (recurse) { Node n, n2; for (n = first, n2 = node.first; n != null; n = n.next, n2 = n2.next) { if (!n.isEquivalentTo(n2, compareJsType, true)) { return false; } } } return true; } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> function or constructor call's side effect flags. * This property is only meaningful for {@link Token#CALL} and * {@link Token#NEW} nodes. */ public void setSideEffectFlags(int flags) { Preconditions.checkArgument( getType() == Token.CALL || getType() == Token.NEW, "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + Token.name(getType())); putIntProp(SIDE_EFFECT_FLAGS, flags); } public void setSideEffectFlags(SideEffectFlags flags) { setSideEffectFlags(flags.valueOf()); } /** * Returns the side effects flags for this node. */ public int getSideEffectFlags() { return getIntProp(SIDE_EFFECT_FLAGS); } /** * A helper class for getting and setting the side-effect flags. * @author johnlenz@google.com (John Lenz) */ public static class SideEffectFlags { private int value = Node.SIDE_EFFECTS_ALL; public SideEffectFlags() { } public SideEffectFlags(int value) { this.value = value; } public int valueOf() { return value; } /** All side-effect occur and the returned results are non-local. */ public void setAllFlags() { value = Node.SIDE_EFFECTS_ALL; } /** No side-effects occur and the returned results are local. */ public void clearAllFlags() { value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS; } public boolean areAllFlagsSet() { return value == Node.SIDE_EFFECTS_ALL; } /** * Preserve the return result flag, but clear the others: * no global state change, no throws, no this change, no arguments change */ public void clearSideEffectFlags() { value |= Node.NO_SIDE_EFFECTS; } public void setMutatesGlobalState() { // Modify global means everything must be assumed to be modified. removeFlag(Node.FLAG_GLOBAL_STATE_UNMODIFIED); removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); removeFlag(Node.FLAG_THIS_UNMODIFIED); } public void setThrows() { removeFlag(Node.FLAG_NO_THROWS); } public void setMutatesThis() { removeFlag(Node.FLAG_THIS_UNMODIFIED);

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>/ public boolean isAdd() { return this.getType() == Token.ADD; } public boolean isAnd() { return this.getType() == Token.AND; } public boolean isArrayLit() { return this.getType() == Token.ARRAYLIT; } public boolean isAssign() { return this.getType() == Token.ASSIGN; } public boolean isAssignAdd() { return this.getType() == Token.ASSIGN_ADD; } public boolean isBlock() { return this.getType() == Token.BLOCK; } public boolean isBreak() { return this.getType() == Token.BREAK; } public boolean isCall() { return this.getType() == Token.CALL; } public boolean isCase() { return this.getType() == Token.CASE; } public boolean isCatch() { return this.getType() == Token.CATCH; } public boolean isComma() { return this.getType() == Token.COMMA; } public boolean isContinue() { return this.getType() == Token.CONTINUE; } public boolean isDebugger() { return this.getType() == Token.DEBUGGER; } public boolean isDec() { return this.getType() == Token.DEC; } public boolean isDefaultCase() { return this.getType() == Token.DEFAULT_CASE; } public boolean isDelProp() { return this.getType() == Token.DELPROP; } public boolean isDo() { return this.getType() == Token.DO; } public boolean isEmpty() { return this.getType() == Token.EMPTY; } public boolean isExprResult() { return this.getType() == Token.EXPR_RESULT; } public boolean isFalse() { return this.getType() == Token.FALSE; } public boolean isFor() { return this.getType() == Token.FOR; } public boolean isFunction() { return this.getType() == Token.FUNCTION; } public boolean isGetterDef() { return this.getType() == Token.GETTER_DEF; } public boolean isGetElem() { return this.getType() == Token.GETELEM; } public boolean isGetProp() { return this.getType() == Token.GETPROP; } public boolean isHook() { return this.getType() == Token.HOOK; } public boolean isIf() { return this.getType

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>() == Token.IF; } public boolean isIn() { return this.getType() == Token.IN; } public boolean isInc() { return this.getType() == Token.INC; } public boolean isInstanceOf() { return this.getType() == Token.INSTANCEOF; } public boolean isLabel() { return this.getType() == Token.LABEL; } public boolean isLabelName() { return this.getType() == Token.LABEL_NAME; } public boolean isName() { return this.getType() == Token.NAME; } public boolean isNE() { return this.getType() == Token.NE; } public boolean isNew() { return this.getType() == Token.NEW; } public boolean isNot() { return this.getType() == Token.NOT; } public boolean isNull() { return this.getType() == Token.NULL; } public boolean isNumber() { return this.getType() == Token.NUMBER; } public boolean isObjectLit() { return this.getType() == Token.OBJECTLIT; } public boolean isOr() { return this.getType() == Token.OR; } public boolean isParamList() { return this.getType() == Token.PARAM_LIST; } public boolean isRegExp() { return this.getType() == Token.REGEXP; } public boolean isReturn() { return this.getType() == Token.RETURN; } public boolean isScript() { return this.getType() == Token.SCRIPT; } public boolean isSetterDef() { return this.getType() == Token.SETTER_DEF; } public boolean isString() { return this.getType() == Token.STRING; } public boolean isStringKey() { return this.getType() == Token.STRING_KEY; } public boolean isSwitch() { return this.getType() == Token.SWITCH; } public boolean isThis() { return this.getType() == Token.THIS; } public boolean isThrow() { return this.getType() == Token.THROW; } public boolean isTrue() { return this.getType() == Token.TRUE; } public boolean isTry() { return this.getType() == Token.TRY; } public boolean isTypeOf() { return this.getType() == Token.TYPEOF; } public boolean isVar() {

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> return this.getType() == Token.VAR; } public boolean isVoid() { return this.getType() == Token.VOID; } public boolean isWhile() { return this.getType() == Token.WHILE; } public boolean isWith() { return this.getType() == Token.WITH; } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> convention){ this.convention = convention; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: String providedClassName = codingConvention.extractClassNameIfProvide(n, parent); if (providedClassName != null) { provides.put(providedClassName, n); } break; case Token.FUNCTION: visitFunctionNode(n, parent); break; case Token.SCRIPT: visitScriptNode(t, n); } } private void visitFunctionNode(Node n, Node parent) { Node name = null; JSDocInfo info = parent.getJSDocInfo(); if (info != null && info.isConstructor()) { name = parent.getFirstChild(); } else { // look to the child, maybe it's a named function info = n.getJSDocInfo(); if (info != null && info.isConstructor()) { name = n.getFirstChild(); } } if (name != null && name.isQualifiedName()) { String qualifiedName = name.getQualifiedName(); if (!this.convention.isPrivate(qualifiedName)) { Visibility visibility = info.getVisibility(); if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) { ctors.put(qualifiedName, name); } } } } private void visitScriptNode(NodeTraversal t, Node n) { for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) { String ctor = ctorEntry.getKey(); int index = -1; boolean found = false; do { index = ctor.indexOf('.', index +1); String provideKey = index == -1 ? ctor : ctor.substring(0, index); if (provides.containsKey(provideKey)) { found = true; break; } } while (index != -1); if (!found) { compiler.report( t.makeError(ctorEntry.getValue(), checkLevel, MISSING_PROVIDE_WARNING, ctorEntry.getKey())); } } provides.clear(); ctors.clear(); } } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> Creates an interface function type. * @param name the function's name * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. */ public FunctionType createInterfaceType(String name, Node source) { return FunctionType.forInterface(this, name, source); } /** * Creates a parameterized type. */ public ParameterizedType createParameterizedType( ObjectType objectType, JSType parameterType) { return new ParameterizedType(this, objectType, parameterType); } /** * Creates a named type. */ @VisibleForTesting public JSType createNamedType(String reference, String sourceName, int lineno, int charno) { return new NamedType(this, reference, sourceName, lineno, charno); } /** * Identifies the name of a typedef or enum before we actually declare it. */ public void identifyNonNullableName(String name) { Preconditions.checkNotNull(name); nonNullableTypeNames.add(name); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { // If the type expression doesn't contain any names, just // resolve it anyway. boolean hasNames = hasTypeName(n); if (hasNames) { return new UnresolvedTypeExpression(this, n, sourceName); } } return createFromTypeNodesInternal(n, sourceName, scope); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, StaticScope<JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes(

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof ObjectType) && !(nonNullableTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodesInternal( typeList.getLastChild(), sourceName, scope); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JS

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Type indexType = createFromTypeNodesInternal( typeList.getFirstChild(), sourceName, scope); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; boolean isConstructor = false; Node current = n.getFirstChild(); if (current.getType() == Token.THIS || current.getType() == Token.NEW) { Node contextNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodesInternal( contextNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0( current.getType() == Token.THIS ? "msg.jsdoc.function.thisnotobject" : "msg.jsdoc.function.newnotobject"), sourceName, contextNode.getLineno(), contextNode.getCharno()); } isConstructor = current.getType() == Token.NEW; current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.PARAM_LIST) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope); if (arg.getType() == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeName(String name) { templateTypeName = name; templateType = new TemplateType(this, name); } /** * Clears the template type name. */ public void clearTemplateTypeName() {

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> case Token.BLOCK: case Token.SCRIPT: case Token.TRY: return true; case Token.FUNCTION: // A function node represents the start of a function where the name // is bleed into the local scope and parameters has been assigned // to the formal argument names. The node includes the name of the // function and the LP list since we assume the whole set up process // is atomic without change in control flow. The next change of // control is going into the function's body represent by the second // child. return n != parent.getFirstChild().getNext(); case Token.WHILE: case Token.DO: case Token.IF: // Theses control structure is represented by its node that holds the // condition. Each of them is a branch node based on its condition. return NodeUtil.getConditionExpression(parent) != n; case Token.FOR: // The FOR(;;) node differs from other control structure in that // it has a initialization and a increment statement. Those // two statements have its corresponding CFG nodes to represent them. // The FOR node represents the condition check for each iteration. // That way the following: // for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to // var x = 0; while(x<10) { x++; } if (NodeUtil.isForIn(parent)) { // TODO(user): Investigate how we should handle the case where // we have a very complex expression inside the FOR-IN header. return n != parent.getFirstChild(); } else { return NodeUtil.getConditionExpression(parent) != n; } case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.WITH: return n != parent.getFirstChild(); default: return false; } } }

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>, TYPE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); declareNativeValueType(s, "undefined", VOID_TYPE); // ActiveXObject is uniquely special, because it can be used to construct // any type (the type that it creates is related to the arguments you // pass to it). declareNativeValueType(s, "ActiveXObject", NO_OBJECT_TYPE); return s; } private void declareNativeFunctionType(Scope scope, JSTypeNative tId) { FunctionType t = typeRegistry.getNativeFunctionType(tId); declareNativeType(scope, t.getInstanceType().getReferenceName(), t); declareNativeType( scope, t.getPrototype().getReferenceName(), t.getPrototype()); } private void declareNativeValueType(Scope scope, String name, JSTypeNative tId) { declareNativeType(scope, name, typeRegistry.getNativeType(tId)); } private void declareNativeType(Scope scope, String name, JSType t) { scope.declare(name, null, t, null, false); } private static class DiscoverEnumsAndTypedefs extends AbstractShallowStatementCallback { private final JSTypeRegistry registry; DiscoverEnumsAndTypedefs(JSTypeRegistry registry) { this.registry = registry; } @Override public void visit(NodeTraversal t, Node node, Node parent) { Node nameNode = null; switch (node.getType()) { case Token.VAR: for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { identifyNameNode( child, child.getFirstChild(), NodeUtil.getBestJSDocInfo(child)); } break; case Token.EXPR_RESULT: Node firstChild = node.getFirstChild(); if (firstChild.isAssign()) { identifyNameNode( firstChild.getFirstChild(), firstChild.getLastChild(), firstChild.getJSDocInfo()); } else { identifyNameNode( firstChild, null, firstChild.getJSDocInfo()); } break; } } private void identifyNameNode( Node nameNode, Node valueNode, JSDocInfo info) { if (nameNode.isQualifiedName()) { if (info != null) { if (info.hasEnumParameterType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.has

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Id(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child, n); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(t, n); switch (n.getType()) { case Token.CALL: checkForClassDefiningCalls(t, n, parent); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n, parent); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n, parent); break; case Token.VAR: defineVar(n, parent); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() && n.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null); } break; } // Analyze any @lends object literals in this statement. if (n

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>.getParent() != null && NodeUtil.isStatement(n) && lentObjectLiterals != null) { for (Node objLit : lentObjectLiterals) { defineObjectLiteral(objLit); } lentObjectLiterals.clear(); } } private void attachLiteralTypes(NodeTraversal t, Node n) { switch (n.getType()) { case Token.NULL: n.setJSType(getNativeType(NULL_TYPE)); break; case Token.VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.STRING: n.setJSType(getNativeType(STRING_TYPE)); break; case Token.NUMBER: n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.OBJECTLIT: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.getLendsName() != null) { if (lentObjectLiterals == null) { lentObjectLiterals = Lists.newArrayList(); } lentObjectLiterals.add(n); } else { defineObjectLiteral(n); } break; // NOTE(nicksantos): If we ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(Node objectLit) { // Handle the @lends annotation. JSType type = null; JSDocInfo info = objectLit.getJSDocInfo(); if (info != null && info.getLendsName() != null) { String lendsName = info.getLendsName(); Var lendsVar = scope.getVar(lendsName); if (lendsVar == null) { compiler.report( JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); } else { type = lendsVar.getType(); if (type == null) { type = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) { compiler.report(

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS>Lit, qualifiedName, keyType, inferred); } else if (keyType != null) { setDeferredType(keyNode, keyType); } if (keyType != null && objLitType != null && declareOnOwner) { // Declare this property on its object literal. boolean isExtern = keyNode.isFromExterns(); objLitType.defineDeclaredProperty(memberName, keyType, keyNode); } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.isGetProp() ? node.getFirstChild() : NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's OK to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch(Node n, Node parent) { assertDefinitionNode(n, Token.CATCH); Node catchName = n.getFirstChild(); defineSlot(catchName, n, null); } /** * Defines a VAR initialization. */ void defineVar(Node n, Node parent) { assertDefinitionNode(n, Token.VAR); JSDocInfo info = n.getJSDocInfo(); if (n.hasMoreThanOneChild()) { if (info != null) { // multiple children compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF)); } for (Node

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> name : n.children()) { defineName(name, n, parent, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, parent, (info != null) ? info : name.getJSDocInfo()); } } /** * Defines a function literal. */ void defineFunctionLiteral(Node n, Node parent) { assertDefinitionNode(n, Token.FUNCTION); // Determine the name and JSDocInfo and l-value for the function. // Any of these may be null. Node lValue = NodeUtil.getBestLValue(n); JSDocInfo info = NodeUtil.getBestJSDocInfo(n); String functionName = NodeUtil.getBestLValueName(lValue); FunctionType functionType = createFunctionTypeFromNodes(n, functionName, info, lValue); // Assigning the function type to the function node setDeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param parent {@code var}'s parent. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, Node parent, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(sourceName, info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? getNativeType(UNKNOWN_TYPE) : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> * Find the ObjectType associated with the given slot. * @param slotName The name of the slot to find the type in. * @return An object type, or null if this slot does not contain an object. */ private ObjectType getObjectSlot(String slotName) { Var ownerVar = scope.getVar(slotName); if (ownerVar != null) { JSType ownerVarType = ownerVar.getType(); return ObjectType.cast(ownerVarType == null ? null : ownerVarType.restrictByNotNullOrUndefined()); } return null; } /** * Resolve any stub declarations to unknown types if we could not * find types for them during traversal. */ void resolveStubDeclarations() { for (StubDeclaration stub : stubDeclarations) { Node n = stub.node; Node parent = n.getParent(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); String ownerName = stub.ownerName; boolean isExtern = stub.isExtern; if (scope.isDeclared(qName, false)) { continue; } // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); ObjectType unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); defineSlot(n, parent, unknownType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( propName, unknownType, n); } else { typeRegistry.registerPropertyOnType( propName, ownerType == null ? unknownType : ownerType); } } } /** * Collects all declared properties in a function, and * resolves them relative to the global scope. */ private final class CollectProperties extends AbstractShallowStatementCallback { private final ObjectType thisType; CollectProperties(ObjectType thisType) { this.thisType = thisType; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isExprResult()) { Node child = n.getFirstChild(); switch (child.getType()) { case Token.ASSIGN: maybeCollectMember(t, child.getFirstChild(), child, child.getLastChild());

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> break; case Token.GETPROP: maybeCollectMember(t, child, child, null); break; } } } private void maybeCollectMember(NodeTraversal t, Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no JSDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || !member.isGetProp() || !member.getFirstChild().isThis()) { return; } member.getFirstChild().setJSType(thisType); JSType jsType = getDeclaredType(t.getSourceName(), info, member, value); Node name = member.getLastChild(); if (jsType != null && (name.isName() || name.isString())) { thisType.defineDeclaredProperty( name.getString(), jsType, member); } } } // end CollectProperties } /** * A stub declaration without any type information. */ private static final class StubDeclaration { private final Node node; private final boolean isExtern; private final String ownerName; private StubDeclaration(Node node, boolean isExtern, String ownerName) { this.node = node; this.isExtern = isExtern; this.ownerName = ownerName; } } /** * A shallow traversal of the global scope to build up all classes, * functions, and methods. */ private final class GlobalScopeBuilder extends AbstractScopeBuilder { private GlobalScopeBuilder(Scope scope) { super(scope); } /** * Visit a node in the global scope, and add anything it declares to the * global symbol table. * * @param t The current traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); switch (n.getType()) { case Token.VAR: // Handle typedefs. if (n.hasOneChild()) { checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); } break; } } @Override void maybeDeclareQualifiedName( Node

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> ObjectType typeOfThis, String templateTypeName, boolean isConstructor, boolean nativeType) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), nativeType); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkNotNull(arrowType); this.source = source; this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY; if (isConstructor) { this.typeOfThis = typeOfThis != null ? typeOfThis : new InstanceObjectType(registry, this, nativeType); } else { this.typeOfThis = typeOfThis != null ? typeOfThis : registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } this.call = arrowType; this.templateTypeName = templateTypeName; } /** Creates an instance for a function that is an interface. */ private FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source) { return new FunctionType(registry, name, source); } @Override public boolean isInstanceType() { // The universal constructor is its own instance, bizarrely. return isEquivalentTo(registry.getNativeType(U2U_CONSTRUCTOR_TYPE)); } @Override public boolean isConstructor() { return kind == Kind.CONSTRUCTOR; } @Override public boolean isInterface() { return kind == Kind.INTERFACE; } @Override public boolean isOrdinaryFunction() { return kind == Kind.ORDINARY; } @Override public FunctionType toMaybeFunctionType() { return this; } @Override public boolean canBeCalled() { return true; } public boolean hasImplementedInterfaces() { if (!implementedInterfaces

Closure, 24

<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); int type = n.getType(); Node parent = n.getParent(); <CHANGES> if (parent.isVar()) { if (n.hasChildren() && n.getFirstChild().isQualifiedName()) { <CHANGEE> String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); <CHANGES> <CHANGEE> // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. <CHANGES> <CHANGEE> // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); <CHANGES> } <CHANGEE> } } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first <FILEE> <SCANS> "apply" function lazily. FunctionParamBuilder builder = new FunctionParamBuilder(registry); // ECMA-262 says that apply's second argument must be an Array // or an arguments object. We don't model the arguments object, // so let's just be forgiving for now. // TODO(nicksantos): Model the Arguments object. builder.addOptionalParams( registry.createNullableType(getTypeOfThis()), registry.createNullableType( registry.getNativeType(JSTypeNative.OBJECT_TYPE))); defineDeclaredProperty(name, new FunctionBuilder(registry) .withParams(builder) .withReturnType(getReturnType()) .build(), source); } } return super.getPropertyType(name); } /** * Get the return value of calling "bind" on this function * with the specified number of arguments. * * If -1 is passed, then we will return a result that accepts * any parameters. */ public FunctionType getBindReturnType(int argsToBind) { FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(getReturnType()); if (argsToBind >= 0) { Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); for (int i = 1; i < argsToBind && params.getFirstChild() != null; i++) { if (params.getFirstChild().isVarArgs()) { break; } params.removeFirstChild(); } builder.withParamsNode(params); } } return builder.build(); } /** * Notice that "call" and "bind" have the same argument signature, * except that all the arguments of "bind" (except the first) * are optional. */ private FunctionType getCallOrBindSignature(boolean isCall) { boolean isBind = !isCall; FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(isCall ? getReturnType() : getBindReturnType(-1)); Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType( registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); thisTypeNode.setOptionalArg(isCall